Crafty.CQRS 1.1.0

dotnet add package Crafty.CQRS --version 1.1.0
NuGet\Install-Package Crafty.CQRS -Version 1.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Crafty.CQRS" Version="1.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Crafty.CQRS --version 1.1.0
#r "nuget: Crafty.CQRS, 1.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Crafty.CQRS as a Cake Addin
#addin nuget:?package=Crafty.CQRS&version=1.1.0

// Install Crafty.CQRS as a Cake Tool
#tool nuget:?package=Crafty.CQRS&version=1.1.0

Crafty.CQRS

Status Version

A simple and crafty library to dispatch command and query to appropriate handlers.

Only a syntactical sugar on top of the MediatR library

One of the main aspect of Domain Driven Design, is to align the language with the domain we want to model.

Why don't we do the same for our technical architecture?

When using Command and Query Responsability Segregation (CQRS) pattern, we would love to have Command and Query defined in our code base, not an implementation details IRequest that could be sometimes the first or the second.

This is the purpose of this library: abstract MediatR IRequest into ICommand and IQuery, keeping all features of this library.

Advantages

All is about readability 😌.

  • align the language: stop using the "request" keyword when you are doing CQRS
  • everything is based on interfaces (thanks to default implementation in interface) :
    • you prefer a record for you command and your handler? Please do it, you have no limitation.
    • you want to implement multiple handling in a single class? please do it.
  • simplify: remove CancellationToken from Handle() method.
    • most of process cannot be cancelled (or we don't want to implement a cancellation process).
  • simplify bis: hide the Unit.Value concept of MediatR

Schema of the 2 flows

flowchart TB
    classDef query fill:#DFFFE3

    CD[ICommandDispatcher] -->|dispatches| C
    C[ICommand] -.->|handled by a single| CH
    CH[ICommandHandler<>]

    QD[IQueryDispatcher] -->|dispatches| Q
    Q[IQuery<>] -.->|handled by a single| QH
    QH[IQueryHandler<>]

    class QD,Q,QH query;
    

Show me code, no talk.

Installation

services
    .AddCqrs(options => options.RegisterServicesFromAssemblyContaining<XXX>())

Command dispatching from api controller

public class UserController : ControllerBase
{
    private ICommandDispatcher _commandDispatcher;
    
    public UserController(ICommandDispatcher commandDispatcher) => _commandDispatcher = commandDispatcher;
  
    [HttpPost]
    public async Task<IActionResult> RegisterUser()
    {
        await _commandDispatcher.Dispatch(new RegisterUserCommand());
    }
}

The command :

public record RegisterUserCommand : ICommand;

The handler :

public record RegisterUserCommandHandler : ICommandHandler<RegisterUserCommand>
{

    public Task Handle(RegisterUserCommand command)
    {
        ...
    }
}

Query dispatching from api controller

public class UserController : ControllerBase
{
    private IQueryDispatcher _queryDispatcher;
    
    public UserController(IQueryDispatcher queryDispatcher) => _queryDispatcher = queryDispatcher;
  
    [HttpPost]
    public async Task<IActionResult> ListAllRegisteredUsers()
    {
        var users = await _queryDispatcher.Dispatch(new ListAllRegisteredUsersQuery());
        
        return Ok(users);
    }
}

The query :

public record ListAllRegisteredUsersQuery : IQuery<IEnumerable<UserListItem>>;

The handler :

public record ListAllRegisteredUsersQueryHandler : IQueryHandler<ListAllRegisteredUsersQuery, IEnumerable<UserListItem>>
{

    public Task<IEnumerable<UserListItem>> Handle(ListAllRegisteredUsersQuery query)
    {
        ...
    }
}

And ... cancellation?

In certain case, if you want to access the CancellationToken in an handler, you can implement ICommandCancellableHandler or IQueryCancellableHandler, that add the token as a second parameter.

public record RegisterUserCommandHandler : ICommandCancellableHandler<RegisterUserCommand>
{

    public Task Handle(RegisterUserCommand command, CancellationToken token)
    {
        ...
    }
}

Using processors

You can implement ICommandPreProcessor and ICommandPostProcessor to interact before and after the ICommandHandler. The same can be achieved for queries.

flowchart TB
    classDef optionalCommand stroke-dasharray: 2 2
    classDef query fill:#DFFFE3
    classDef optionalQuery fill:#DFFFE3,stroke-dasharray: 2 2

    CD[ICommandDispatcher] -->|dispatches| C
    C[ICommand] -.->|is preprocessed| CPre
    CPre[ICommandPreProcessor<>] -->|handled by a single| CH
    CH[ICommandHandler<>] -.->|is post processed| CPost
    CPost[ICommandPostProcessor<>]

    QD[IQueryDispatcher] -->|dispatches| Q
    Q[IQuery<>] -.->|is preprocessed| QPre
    QPre[IQueryPreProcessor<>] -->|handled by a single| QH
    QH[IQueryHandler<>] -.->|is post processed| QPost
    QPost[IQueryPostProcessor<>]

    class CPre,CPost optionalCommand;
    class QD,Q,QPre,QH,QPost query;
    class QPre,QPost optionalQuery;
    
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.0 128 6/5/2023
1.0.0 125 6/5/2023