Branch | Status |
---|---|
Master | |
Dev |
Simple CQRS library
This project composes of components for implementing the CQRS pattern (Command Handling). This library was built with simplicity, modularity and pluggability in mind.
- Send commands to registered command handlers.
- Provides simple abstraction for hosted command handlers which can be registered just like an regular command handler.
- Multiple ways of registering command handlers:
-
Simple handler registration (no IoC container).
-
IoC container registration
-
Attribute registration
-
achieved by marking methods with [CommandHandler] attributes from the Xer.Cqrs.CommandStack.Extensions.Attributes package.
-
See https://github.com/XerProjects/Xer.Cqrs.CommandStack.Extensions.Attributes for documentation.
-
-
You can simply clone this repository, build the source, reference the dll from the project, and code away!
Xer.Cqrs.CommandStack library is available as a Nuget package:
To install Nuget packages:
- Open command prompt
- Go to project directory
- Add the packages to the project:
dotnet add package Xer.Cqrs.CommandStack
- Restore the packages:
dotnet restore
(Samples are in ASP.NET Core)
// Example command.
public class RegisterProductCommand
{
public int ProductId { get; }
public string ProductName { get; }
public RegisterProductCommand(int productId, string productName)
{
ProductId = productId;
ProductName = productName;
}
}
// Command handler.
public class RegisterProductCommandHandler : ICommandAsyncHandler<RegisterProductCommand>
{
private readonly IProductRepository _productRepository;
public RegisterProductCommandHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[CommandHandler] // This is in Xer.Cqrs.CommandStack.Extensions.Attributes. This allows the method to registered as a command handler through attribute registration.
public Task HandleAsync(RegisterProductCommand command, CancellationToken cancellationToken = default(CancellationToken))
{
return _productRepository.SaveAsync(new Product(command.ProductId, command.ProductName));
}
}
Before we can delegate any commands, first we need to register our command handlers. There are several ways to do this:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
// Repository.
services.AddSingleton<IProductRepository, InMemoryProductRepository>();
// Register command delegator.
services.AddSingleton<CommandDelegator>((serviceProvider) =>
{
// Allows registration of a single message handler per message type.
var registration = new SingleMessageHandlerRegistration();
registration.RegisterCommandHandler(() => new RegisterProductCommandHandler(serviceProvider.GetRequiredService<IProductRepository>()));
return new CommandDelegator(registration.BuildMessageHandlerResolver());
});
...
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
// Repository.
services.AddSingleton<IProductRepository, InMemoryProductRepository>();
// Register command handlers to the container.
// The AddCqrs extension method is in Xer.Cqrs.Extensions.Microsoft.DependencyInjection package.
services.AddCqrs(typeof(RegisterProductCommandHandler).Assembly);
...
}
After setting up the command delegator in the IoC container, commands can now be delegated by simply doing:
...
private readonly CommandDelegator _commandDelegator;
public ProductsController(CommandDelegator commandDelegator)
{
_commandDelegator = commandDelegator;
}
// POST api/products
[HttpPost]
public async Task<IActionResult> RegisterProduct([FromBody]RegisterProductCommandDto model)
{
RegisterProductCommand command = model.ToDomainCommand();
await _commandDelegator.SendAsync(command);
return Accepted();
}
...