I want to introduce a simple command handler pattern in order to separate commands and queries of your applications if it’s needed. Sometimes especially in large-scale applications, you need to have two layers for reading and write separated and assign two teams to work on them.
You can use the pattern in this post based on the complexity and needs of your application. I recommend that don’t call this CQRS pattern as it’s something completely different and more complex.
Get started and Create an interface with no any member:
public interface ICommand { }
You will see later in this blog post that ICommand is just a flag to determine the commands for the handler. Every command which is trying to make a change in application’s state should return a result. so create another interface named ICommandResult
public interface ICommandResult { string Message { get; } bool Success { get; } }
implement the above interface as CommandResult and derive the FailureResult and SuccessResult classes from:
internal abstract class CommandResult : ICommandResult { public string Message { get; protected set; } public bool Success { get; protected set; } } internal class FailureResult : CommandResult { public FailureResult(string message) { Success = false; Message = message; } } internal class SuccessResult : CommandResult { public SuccessResult(string message) { Success = true; Message = message; } }
Now create an interface for handling the validations in CommandBus class:
internal interface IValidationHandler<in TCommand> where TCommand : ICommand { IEnumerable<ValidationResult> Validate(TCommand command); }
And create a class for validations:
public class ValidationResult { /// <summary> /// Initializes a new instance of the <see cref="ValidationResult"/> class. /// </summary> public ValidationResult() { } /// <summary> /// Initializes a new instance of the <see cref="ValidationResult"/> class. /// </summary> /// <param name="memeberName">Name of the memeber.</param> /// <param name="message">The message.</param> public ValidationResult(string memeberName, string message) { MemberName = memeberName; Message = message; } /// <summary> /// Initializes a new instance of the <see cref="ValidationResult"/> class. /// </summary> /// <param name="message">The message.</param> public ValidationResult(string message) { Message = message; } /// <summary> /// Gets or sets the name of the member. /// </summary> /// <value> /// The name of the member. May be null for general validation issues. /// </value> public string MemberName { get; set; } /// <summary> /// Gets or sets the message. /// </summary> /// <value> /// The message. /// </value> public string Message { get; set; } }
And now create the most used interface in this pattern. ICommandBus will be called as bus handler in every command:
public interface ICommandBus { ICommandResult Send<TCommand>(TCommand command) where TCommand : ICommand; IEnumerable<ValidationResult> Validate<TCommand>(TCommand command) where TCommand : ICommand; }
To implementing this interface, create a class again with the following definition:
internal class CommandBus : ICommandBus { public ICommandResult Send<TCommand>(TCommand command) where TCommand : ICommand { var handler = DependencyResolver.Current.GetService<ICommandHandler<TCommand>>(); if (!((handler != null) && handler != null)) { 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 != null)) { throw new ValidationHandlerNotFoundException(typeof(TCommand)); } return handler.Validate(command); } }
The above class will handle every command received from the bus. And now for executing the handler or so-called for implementing the business, you need an interface:
internal interface ICommandHandler<in TCommand> where TCommand : ICommand { ICommandResult Execute(TCommand command); }
And finally as I’m personally interested in to have my own exception handler, so I create a customized handler:
public class CommandHandlerNotFoundException : Exception { public CommandHandlerNotFoundException(Type type) : base(string.Format("Command handler not found for command type: {0}", type)) { } } internal class ValidationHandlerNotFoundException : Exception { public ValidationHandlerNotFoundException(Type type) : base(string.Format("Validation handler not found for command type: {0}", type)) { } }
Finished! Now let’s use the above codes in a real work application. We are going to handle the products of an e-commerce application as its business is easier to comprehend for everybody:
public interface IProductCommand : ICommand { string Name { get; set; } string OriginalName { get; set; } string Description { get; set; } decimal Price { get; set; } } public class ProductCommand : IProductCommand { public string Name { get; set; } public string OriginalName { get; set; } public string Description { get; set; } public decimal Price { get; set; } } public class CreateProductCommand : ProductCommand { } public class EditProductCommand : ProductCommand { public Guid ProductId { get; set; } public DateTime CreationDate { get; set; } public DateTime LastUpdateDate { get; set; } } public class DeleteProductCommand : ICommand { public Guid[] ProductIds { get; set; } }
You can see that I created an interface derived from ICommand named IProductCommand and finally created the target classes. And at the end we should create the Product handler class:
internal partial class ProductCommandHandler : ICommandHandler<CreateProductCommand>, ICommandHandler<EditProductCommand>, ICommandHandler<DeleteProductCommand> { private readonly IProductRepository _productRepository; private readonly IMembershipRepository _membershipRepository; private readonly IUnitOfWork _unitOfWork; private readonly ICacheManager _cacheManager; private readonly ILogger _logger; public ProductCommandHandler( IProductRepository productRepository, IUnitOfWork unitOfWork, ILogger logger, ICacheManager cacheManager) { _productRepository = productRepository; _unitOfWork = unitOfWork; _logger = logger; _cacheManager = cacheManager; } public ICommandResult Execute(CreateProductCommand command) { try { if (command == null) { throw new ArgumentNullException(); } var product = new Product(); AddProductAppurtenance(command, product); _productRepository.Add(product); _unitOfWork.Commit(); return new SuccessResult(ProductCommandMessage.ProductCreatedSuccessfully); } catch (Exception exception) { _logger.Error(exception.Message); return new FailureResult(ProductCommandMessage.ProductCreationFailed); } } public ICommandResult Execute(EditProductCommand command) { try { if (command == null) { throw new ArgumentNullException(); } var product = _productRepository.GetProductDetailById(command.ProductId); ClearProductAppurtenance(product); AddProductAppurtenance(command, product); _productRepository.Edit(product); _unitOfWork.Commit(); return new SuccessResult(ProductCommandMessage.ProductEditedSuccessfully); } catch (Exception exception) { _logger.Error(exception.Message); return new FailureResult(ProductCommandMessage.ProductEditionFailed); } } public ICommandResult Execute(DeleteProductCommand command) { if (command.ProductIds == null) { throw new ArgumentNullException(); } var exceptions = new List<Exception>(); foreach (var productId in command.ProductIds) { try { var product = DeleteProduct(productId); _productRepository.Edit(product); } catch (Exception exception) { exceptions.Add(exception); return new FailureResult(ProductCommandMessage.ProductDeletionFailed); } } if (exceptions.Any()) { throw new AggregateException(exceptions); } _unitOfWork.Commit(); return new SuccessResult(ProductCommandMessage.ProductsDeletedSuccessufully); }
We used all of the classes introduced in infrastructure.
Note 1: so you need to have an IOC container tool, to the best of my Knowledge, AsClosedTypesOf in Autofac resolves the ICommandHandler. This a piece of code I wrote for IOC handler:
public class CommandModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterModule<RepositoryModule>(); builder.RegisterType<CommandBus>().As<ICommandBus>().InstancePerRequest(); builder.RegisterAssemblyTypes(ThisAssembly).AsClosedTypesOf(typeof(ICommandHandler<>)).InstancePerRequest(); } }
Note 2: a penny for your thoughts! how to use ICommandBus!? See the below piece of code as an example:
[HttpPost] public JsonResult DeleteProduct(DeleteProductCommand command) { var result = _commandBus.Send(command); return JsonMessage(result); }
have fun.
Category: Software
Tags: Design Pattern Architecture