ardalis / ardalis/Ardalis.SharedKernel
Align SharedKernel with CleanArchitecture's Result Pattern
- Dominant language
- C#
- Stars
- 177
- Forks
- 80
- PR merge metrics
- No merged PRs in 30d
Description
Hi @ardalis,
I've created a PR #13 that introduces the **Ardalis.Result** into the `ICommand` and `IQuery` interfaces directly in `Ardalis.SharedKernel`. The goal is to provide a standardized way to handle operation using Result Pattern in CQRS-based workflows, improving consistency and readability. I have already implemented this approach in several SharedKernel projects.
### Why This Change?
1. Since `Ardalis.SharedKernel` is meant to serve as a **reference implementation** (copied/modified rather than used directly), it should demonstrate **best practices**. The Result pattern is widely adopted for clear error handling and richer return types.
2. As seen in the [CleanArchitecture repo](https://github.com/ardalis/CleanArchitecture), handlers often return `Result`. By baking this into the `ICommand`/`IQuery` interfaces, we:
- Eliminate repetitive `Result` declarations.
- Make the intent **immediately clear** in derived handlers.
- Encourage consistent error handling.
### Drawbacks
1. Had to remove the `out` keyword from `ICommand` which was originally added for covariant purposes. I attempted to modify `Ardalis.Result` to include `IResult` interface, but that didn't work well - if implemented, it would require `IResult` to be used consistently across all extension methods in additional packages.
```csharp
public interface IResult : IResult
{
T Value { get; }
}
```
However, this change would allow automatic casting to base types, providing a potential benefit. - Currently, developers are forced to use .Map() or .Bind() extension methods, if I'm not mistaken.
2. Had to introduce the `ICommand` with no return type (only the `Result`), and `ICommand` which allow you to return value wrapped with `Result`. Personally, I don't like the idea to return more than created entity Id or domain concept struct, so I have another suggestion to consider. The idea is to have 2 different types of commands: those that return created entity identification information and empty one, returning only the result information:
```csharp
public interface ICommand : IRequest;
// TId should be the strongly typed domain concepts
// representing the ID of the entity, so it needs to have constraints
public interface ICreatedCommand : IRequest> where TId : struct, IComparable, IComparable, IConvertible, IEquatable, IFormattable;
```
## Things To Discuss
1. Does this align with the library's direction?
2. Do you think if is worth to consider alternative approaches for covariance using `IResult` and what are the main drawback in this approach?
3. Do you think the approach with the separate `ICreatedCommand` and `ICommand` can be discussed further and added as an example of more "clear" CQRS pattern?
Contributor guide
No contributing guide indexed for this repository
Research direction
Review PR #13 and the existing ICommand and IQuery interfaces in Ardalis.SharedKernel first. Compare the proposed Ardalis.Result integration, covariance alternative, and separate ICreatedCommand design against the stated library direction. Done means the maintainers select and document one approach or close the discussion with a decision.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100