Plain CQRS is a library that supports implementing CQRS pattern using most popular frameworks, by providing dispatchers implementations out of the box.
Install library via
Package Manager Install-Package PlainCQRS.Core -Version 1.0.0
or
.Net CLI dotnet add package PlainCQRS.Core --version 1.0.0
Create sample command and command handler
public class DoNothing : ICommand
{
}
public class DoNothingHandler : ICommandHandler<DoNothing>
{
public void Handle(DoNothing command)
{
}
}
Register command and command handler in the container
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<DoNothingHandler>()
.As<ICommandHandler<DoNothing>>()
.InstancePerLifetimeScope();
}
Use
public class HomeController : Controller
{
private readonly ICommandSender commandBus;
public HomeController(ICommandSender commandBus)
{
this.commandBus = commandBus;
}
public IActionResult DoSomething()
{
commandBus.Send(new DoNothing());
return RedirectToAction(nameof(Index));
}
//... other actions
}
Create sample query and query handler
public class GetSomething : IQuery<SomethingViewModel>
{
}
public class GetSomethingHandler : IQueryHandler<GetSomething, SomethingViewModel>
{
public SomethingViewModel Handle(GetSomething query)
{
return new SomethingViewModel
{
Thing = "I am the thing"
};
}
}
Register query and query handler in the container
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<GetSomethingHandler>()
.As<IQueryHandler<GetSomething, SomethingViewModel>>()
.InstancePerLifetimeScope();
}
Use
public class HomeController : Controller
{
private readonly IQueryDispatcher queryDispatcher;
public HomeController(IQueryDispatcher queryDispatcher)
{
this.queryDispatcher = queryDispatcher;
}
public IActionResult GetSomething()
{
var viewModel = queryDispatcher.Execute(new GetSomething());
return View(viewModel);
}
}
Create event and event handler
public class SomethingHappened : IEvent
{
public Guid Id => Guid.NewGuid();
// other attributes
}
public class SomethingHappenedHandler : IEventHandler<SomethingHappened>
{
public void Handle(SomethingHappened @event)
{
}
}
Register event and event handler
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<SomethingHappenedHandler>()
.As<IEventHandler<SomethingHappened>>()
.InstancePerLifetimeScope();
}
It is also possible to register more than one handler for particular event
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<NothingHappenedHandlerOne>()
.As<IEventHandler<NotingHappened>>()
.InstancePerLifetimeScope();
builder.RegisterType<NothingHappenedHandlerTwo>()
.As<IEventHandler<NotingHappened>>()
.InstancePerLifetimeScope();
}
Publish event. In this example event is published after handling a DoNothing command from DoNothing handler
public class DoNothingHandler : ICommandHandler<DoNothing>
{
private readonly IEventPublisher eventPublisher;
public DoNothingHandler(IEventPublisher eventPublisher)
{
this.eventPublisher = eventPublisher;
}
public void Handle(DoNothing command)
{
// .. handle command
// publish event
eventPublisher.Publish(new NotingHappened());
}
}
Each handler and dispatcher has its async equivalent listed below.
PlainCQRS.Core.Commands.ICommandHandlerAsync<in TCommand> where TCommand : ICommand
PlainCQRS.Core.Commands.ICommandSenderAsync
PlainCQRS.Core.Queries.IQueryHandlerAsync<in TQuery, TResult> where TQuery : IQuery<TResult>
PlainCQRS.Core.Queries.IQueryDispatcherAsync
PlainCQRS.Core.Events.IEventHandlerAsync<in TEvent> where TEvent : IEvent
PlainCQRS.Core.Events.IEventPublisherAsync
Usage is similar to the synchronous implementations.
public class GetSomethingAsyncHandler : IQueryHandlerAsync<GetSomethingAsync, SomethingViewModel>
{
public async Task<SomethingViewModel> HandleAsync(GetSomethingAsync query,
CancellationToken cancellationToken = default(CancellationToken))
{
// await long running task
}
}
public void ConfigureContainer(ContainerBuilder builder)
{
// other registrations
builder.RegisterType<GetSomethingAsyncHandler>()
.As<IQueryHandlerAsync<GetSomethingAsync, SomethingViewModel>>()
.InstancePerLifetimeScope();
}
public class HomeController : Controller
{
private readonly IQueryDispatcherAsync queryDispatcher;
public HomeController(IQueryDispatcherAsync queryDispatcher)
{
this.queryDispatcher = queryDispatcher;
}
public async Task<IActionResult> GetSomething()
{
var viewModel = await queryDispatcher.ExecuteAsync(new GetSomethingAsync());
return View(viewModel);
}
}
To get started with Plain CQRS in Asp Net Core app using Autofac container simply register PlainCQRS.Autofac.AspNetCoreModule
in
your Autofac container.
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new AspNetCoreModule());
}
This will register default implementations of objects dispatchers. Now you can start registering yours commands/queries/events
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new AspNetCoreModule());
builder.RegisterType<DoNothingHandler>()
.As<ICommandHandler<DoNothing>>()
.InstancePerLifetimeScope();
builder.RegisterType<GetSomethingHandler>()
.As<IQueryHandler<GetSomething, SomethingViewModel>>()
.InstancePerLifetimeScope();
builder.RegisterType<NothingHappenedHandler>()
.As<IEventHandler<NotingHappened>>()
.InstancePerLifetimeScope();
}
I am going to successively add implementations for other frameworks, however it is possible to write own implementation or overwrite default ones. All you need to to is to implement following interfaces and register implementations in your container.
PlainCQRS.Core.Commands.ICommandSender
PlainCQRS.Core.Commands.ICommandSenderAsync
PlainCQRS.Core.Commands.ICommandHandler<in TCommand> where TCommand : ICommand
PlainCQRS.Core.Commands.ICommandHandlerAsync<in TCommand> where TCommand : ICommand
PlainCQRS.Core.Events.IEventPublisher
PlainCQRS.Core.Events.IEventPublisherAsync
PlainCQRS.Core.Events.IEventHandler<TEvent> where TEvent : IEvent
PlainCQRS.Core.Events.IEventHandlerAsync<in TEvent> where TEvent : IEvent
PlainCQRS.Core.Queries.IQueryDispatcher
PlainCQRS.Core.Queries.IQueryDispatcherAsync
PlainCQRS.Core.Queries.IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
PlainCQRS.Core.Queries.IQueryHandlerAsync<in TQuery, TResult> where TQuery : IQuery<TResult>