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/.