Commands, Command Handlers and Command Dispatcher
In this post, I am trying to add some CQRS principles onto
my
EFMVC
project.
EFMVC
is a small web app for demonstrating ASP.NET MVC and EF Code
First. Please keep in mind that this is not the
implementation CQRS patterns, but trying to add some CQRS
flavors on the Solution Architecture with Commands that
changes the data (Create, Update and Delete). The current
implementation of command execution is implemented in a
synchronous way.
CQRS
CQRS is stands for Command-Query Responsibility Segregation
that is a principle of separating commands (that change the
data) from queries (that read the data). The CQRS pattern is
first described by
Greg Young
and his blog post
CQRS, Task Based UIs, Event Sourcing agh!
has explained about this approach. If you want to build real
world CRQS based apps, I highly recommending to read
Rinat Abdullin's blog
and the
CQRS info site.
Domain Entity
The below domain entity Category is using for demo
application
public class Category { public int CategoryId { get; set; } [Required] public string Name { get; set; } public string Description { get; set; } }
Command
Every write operations represent a command and these
commands will be executed by using a Command Handler. Let’s
add command object CreateOrUpdateCategoryCommand for
representing the Create and Update write operation. The
CreateOrUpdateCategoryCommandis is representing the write
operation for the domain entity Category.
public class CreateOrUpdateCategoryCommand : ICommand { public CreateOrUpdateCategoryCommand(int CategoryId,string name, string description) { this.CategoryId = CategoryId; this.Name = name; this.Description = description; } public int CategoryId { get; set; } public string Name { get; set; } public string Description { get; set; } }
The interface ICommand is now just using for IoC
purpose to hook corresponding Command Handler object.
public interface ICommand { }
Command Handler
The following code is shown the Command Handler for
the command object CreateOrUpdateCategoryCommand.
public class CreateOrUpdateCategoryHandler : ICommandHandler<CreateOrUpdateCategoryCommand> { private readonly ICategoryRepository categoryRepository; private readonly IUnitOfWork unitOfWork; public CreateOrUpdateCategoryHandler(ICategoryRepository categoryRepository, IUnitOfWork unitOfWork) { this.categoryRepository = categoryRepository; this.unitOfWork = unitOfWork; } public ICommandResult Execute(CreateOrUpdateCategoryCommand command) { var category = new Category { CategoryId = command.CategoryId, Name = command.Name, Description = command.Description }; if (category.CategoryId == 0) categoryRepository.Add(category); else categoryRepository.Update(category); unitOfWork.Commit(); return new CommandResult(true); } }
The CreateOrUpdateCategoryHandler object will perform the data persistence for the command operation CreateOrUpdateCategoryCommand.
The ICommandHandler<T> interface is shown below
public interface ICommandHandler<in TCommand> where TCommand: ICommand { ICommandResult Execute(TCommand command); }
Command Validator
Every command object would be submitted to a Command
Bus that will be hook the right command handler and will
execute the command operation. In this demo app, we just
validate the command before submitting the command to
command bus.
The following class is using for validating the
business rules for our command CreateOrUpdateCategoryCommand
public class CanAddCategory : IValidationHandler<CreateOrUpdateCategoryCommand> { private readonly ICategoryRepository categoryRepository; public CanAddCategory(ICategoryRepository categoryRepository, IUnitOfWork unitOfWork) { this.categoryRepository = categoryRepository; } public IEnumerable<ValidationResult> Validate( CreateOrUpdateCategoryCommand command) { Category isCategoryExists=null; if(command.CategoryId==0) isCategoryExists = categoryRepository.Get(c => c.Name == command.Name); else isCategoryExists = categoryRepository.Get(c => c.Name == command.Name && c.CategoryId!=command.CategoryId); if (isCategoryExists!=null ) { yield return new ValidationResult("Name", Resources.CategoryExists); } } }
The IValidationHandler<T> interface is shown
below
public interface IValidationHandler<in TCommand> where TCommand : ICommand { IEnumerable<ValidationResult> Validate(TCommand command); }
Command Dispatcher
The responsibility of the Command Dispatcher object is
to hook right Command Handler object based on the command
object we have submitted to the Command Dispatcher object.
Autofac IoC container is using to hook the Command Handler
object for the given command object.
The following is the contract type of Command
Dispatcher
public interface ICommandBus { ICommandResult Submit<TCommand>(TCommand command) where TCommand: ICommand; IEnumerable<ValidationResult> Validate<TCommand>(TCommand command) where TCommand : ICommand; }
The default implementation of the ICommandBus is
shown below
public class DefaultCommandBus : ICommandBus { public ICommandResult Submit<TCommand>(TCommand command) where TCommand: ICommand { var handler = DependencyResolver.Current.GetService<ICommandHandler<TCommand>>(); if (!((handler != null) && handler is ICommandHandler<TCommand>)) { throw new CommandHandlerNotFoundException(typeof(TCommand)); } return handler.Execute(command); } public IEnumerable<ValidationResult> Validate<TCommand>(TCommand command) where TCommand : ICommand { var handler = DependencyResolver.Current.GetService<IValidationHandler<TCommand>>(); if (!((handler != null) && handler is IValidationHandler<TCommand>)) { throw new ValidationHandlerNotFoundException(typeof(TCommand)); } return handler.Validate(command); } }
The command bus provides two methods – Submit and Validate.
The submit method will execute the appropriate command
handler object and the validate method is used for
validating the command object before submitting the command.
The default command bus object provides the command
execution in a synchronous way. In many real world CQRS
implementations, this would be in an asynchronous way.
Component Registration in Autofac
The following code is using in the EFMVC project to register components with Autofac.
private static void SetAutofacContainer() { var builder = new ContainerBuilder(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); builder.RegisterType<DefaultCommandBus>().As<ICommandBus>() .InstancePerHttpRequest(); builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest(); builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>() .InstancePerHttpRequest(); builder.RegisterAssemblyTypes(typeof(CategoryRepository).Assembly) .Where(t => t.Name.EndsWith("Repository")) .AsImplementedInterfaces().InstancePerHttpRequest(); var services = Assembly.Load("EFMVC.Domain"); builder.RegisterAssemblyTypes(services) .AsClosedTypesOf(typeof(ICommandHandler<>)).InstancePerHttpRequest(); builder.RegisterAssemblyTypes(services) .AsClosedTypesOf(typeof(IValidationHandler<>)).InstancePerHttpRequest(); builder.RegisterType<DefaultFormsAuthentication>().As<IFormsAuthentication>() .InstancePerHttpRequest(); builder.RegisterFilterProvider(); IContainer container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); }
ASP.NET MVC Controller
In our demo app, the command object CreateOrUpdateCategoryCommand is creating and submitting to Command Bus from a ASP.NET MVC controller action method. In the following action method, we are creating the CreateOrUpdateCategoryCommand object form values and calling the Validate method of command bus. If the command is valid, we will submit the command object to the Command Bus that will execute the command operation in a synchronous way.[HttpPost] [ValidateAntiForgeryToken] public ActionResult Save(CategoryFormModel form) { if(ModelState.IsValid) { var command = new CreateOrUpdateCategoryCommand(form.CategoryId,form.Name, form.Description); IEnumerable<ValidationResult> errors= commandBus.Validate(command); ModelState.AddModelErrors(errors); if (ModelState.IsValid) { var result = commandBus.Submit(command); if (result.Success) return RedirectToAction("Index"); } } //if fail if (form.CategoryId == 0) return View("Create",form); else return View("Edit", form); }
Source Code
The source code of EFMVC available from http://efmvc.codeplex.com/.