Documentation

CQRS

Commands, queries and event handlers.

Commands

Overview

Adds an ability to create and process commands in the sense of CQRS.

Installation

dotnet add package Convey.CQRS.Commands

Dependencies

Usage

Implement ICommand (marker) interface in the selected class. Since the command represents the user’s intention you should follow the convention:

  • keep all the commands immutable
  • name of your commands should be imperative
public class CreateAccount : ICommand
{
    public Guid Id { get; }
    public string Email { get; }
    public string Password { get; }

    public CreateUser(id, email, password)
    {
        Id = id;
        Email = email;
        Password = password;
    }
}

Create dedicated command handler class that implements ICommandHandler<TCommand> interface with HandleAsync() method:

public class CreateAccountHandler : ICommandHandler<CreateAccount>
{
    public Task HandleAsync(CreateAccount command)
    {
        //put the handling code here
    }
}

You can easily register all command handlers in DI container by calling AddCommandHandlers() method on IConveyBuilder:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    var builder = services.AddConvey()
        .AddCommandHandlers();

    //other registrations    
    return builder.Build();
}

Dispatching a particular command object can be also done using Convey package. Start with registering in-memory dispatcher on your IConveyBuilder by calling a AddInMemoryCommandDispatcher() method:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    var builder = services.AddConvey()
        .AddCommandHandlers()
        .AddInMemoryCommandDispatcher();

    //other registrations    
    return builder.Build();
}

Then simply inject ICommandDispatcher into a class and call DispatchAsync() method:

public class AccountsService
{
    private readonly ICommandDispatcher _dispatcher;

    public AccountsService(ICommandDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    } 

    public Task CreateAccountAsync(CreateAccount command)
        => _dispatcher.DispatchAsync(command);
}

Queries

Overview

Adds an ability to create and process queries in the sense of CQRS.

Installation

dotnet add package Convey.CQRS.Queries

Dependencies

Usage

Implement IQuery<TResult> interface in the selected class:

public class GetAccount : IQuery<AccountDto>
{
    public Guid Id { get; set; }
}

Create dedicated query handler class that implements IQueryHandler<TQuery, TResult> interface with HandleAsync() method:

public class GetAccountHandler : IQueryHandler<GetAccount, AccountDto>
{
    public Task<AccountDto> HandleAsync(GetAccount query)
    {
        //put the handling code here
    }
}

You can easily register all query handlers in DI container by calling AddQueryHandlers() method on IConveyBuilder:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    var builder = services.AddConvey()
        .AddQueryHandlers();

    //other registrations    
    return builder.Build();
}

Dispatching a particular query object can be also done using Convey package. Start with registering in-memory dispatcher on your IConveyBuilder by calling a AddInMemoryQueryDispatcher() method:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    var builder = services.AddConvey()
        .AddQueryHandlers()
        .AddInMemoryQueryDispatcher();

    //other registrations    
    return builder.Build();
}

Then simply inject IQueryDispatcher into a class and call DispatchAsync() method:

public class AccountsService
{
    private readonly IQueryDispatcher _dispatcher;

    public AccountsService(IQueryDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    } 

    public Task<AccountDto> GetAccountAsync(Guid id)
        => _dispatcher.DispatchAsync(new GetAccount { Id = id });
}

Events

Overview

Adds ability to create and process events in the sense of CQRS.

Installation

dotnet add package Convey.CQRS.Events

Dependencies

Usage

Implement IEvent or IRejectedEvent (marker) interface in the selected class. Since the event represents something that already happened, you should follow the convention:

  • keep all the events immutable
  • name of your events should kept in the past tense
public class AccountCreated : IEvent
{
    public Guid Id { get; }

    public AccountCreated(id)
    {
        Id = id;
    }
}

Create dedicated event handler class that implements IEventHandler<TEvent> interface with HandleAsync() method:

public class AccountCreatedHandler : IEventHandler<AccountCreated>
{
    public Task HandleAsync(AccountCreated @event)
    {
        //put the handling code here
    }
}

You can easily register all event handlers in DI container by calling AddEventHandlers() method on IConveyBuilder:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    var builder = services.AddConvey()
        .AddEventHandlers();

    //other registrations    
    return builder.Build();
}

Dispatching a particular event object can be also done using Convey package. Start with registering in-memory dispatcher on your IConveyBuilder by calling a AddInMemoryEventDispatcher() method:

public IServiceProvider ConfigureServices(this IServiceCollection services)
{
    services.AddOpenTracing();

    var builder = services.AddConvey()
        .AddCommandHandlers()
        .AddInMemoryEventDispatcher();

    //other registrations    
    return builder.Build();
}

Then simply inject IEventDispatcher into a class and call DispatchAsync() method:

public class AccountsService
{
    private readonly IEventDispatcher _dispatcher;

    public AccountsService(IEventDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    } 

    public Task PostProcessAccountCreation(AccountCreated @event)
        => _dispatcher.DispatchAsync(@event);
}