Using dispatcher class to resolve CQRS commands and queries in ASP.NET Core
Image from Pixabay

Using dispatcher class to resolve CQRS commands and queries in ASP.NET Core

Injecting dispatcher class instead of multiple commands and queries in controller constructor

Recently I wrote an article about Automatic CQRS handler registration in ASP.NET Core with reflection in order to reduce number of lines for registering command and query to IOC container in ASP.NET Core. However, this solution does reduce lines for service registration in the startup, you still need to add all command and query handlers as parameters to the controller constructor.

public class ProductsController : ControllerBase
{
#region Fields
private readonly ICommandHandler<CreateProductCommand> _createProductHandler;
private readonly IQueryHandler<ProductBrowseQuery, IEnumerable<ProductView>> _browseProductsHandler;
private readonly IQueryHandler<ProductGetQuery, ProductView> _getProductHandler;
#endregion
public ProductsController(
ICommandHandler<CreateProductCommand> createProductHandler,
IQueryHandler<ProductBrowseQuery, IEnumerable<ProductView>> browseProductsHandler,
IQueryHandler<ProductGetQuery, ProductView> getProductHandler
)
{
this._createProductHandler = createProductHandler;
this._browseProductsHandler = browseProductsHandler;
this._getProductHandler = getProductHandler;
}
}
Note

In order to be able to follow code snippets in this article please refer to "Automatic CQRS handler registration in ASP.NET Core with reflection" http://bit.ly/2EmQSQV

In this example which is based on the code from the previously mentioned article, you can see that for each command and query handler we have a parameter in controller constructor. For a simple sample project we already have three parameters. Imagine the number on a large scale project. This can cause potential bug nest in your code not to mention that all parameters for commands and queries would be resolved and initialized even though you wont use them all for a method that will be execute on the request. Same goes for unit tests for your controllers. Here is how a simplest test for API GET method would look like

        [Fact]
        public async Task TestProductsGet_Ok()
        {
            //Arrange
            Mock<IQueryHandler<ProductBrowseQuery, IEnumerable<ProductView>>> queryHandler = new Mock<IQueryHandler<ProductBrowseQuery, IEnumerable<ProductView>>>();
            queryHandler.Setup(s => s.HandleAsync(It.IsAny<ProductBrowseQuery>())).ReturnsAsync(new List<ProductView>());
            var controller = new ProductsController(
                browseProductsHandler: queryHandler.Object,
                createProductHandler:null,
                getProductHandler:null
                );

            //Execute
            var result = await controller.Get(new ProductBrowseQuery() { });

            //Assert
            Assert.NotNull(result);
            Assert.IsAssignableFrom<OkObjectResult>(result);
        }

    

For each method there would be different set of null values for all the command and query handlers which are not used for that method. Not so neat and clean.

Command dispatcher

Now instead of having multiple parameters for commands and queries in the controller, we'll have one which will resolve the handler for a specific type. Since command are immutable and they do not return any result, it makes it easier to start with them as we need to resolve the dependency based on only one generic type used and that is the one that implements ICommand interface.

Of course, all the dependency injections described in article Automatic CQRS handler registration in ASP.NET Core with reflection stay in place. We'll just add some new so that our controller becomes simpler.

First we need an interface for command dispatcher. For the invocation of command, I kept same method name HandleAsync as in the command handler for the simple reason of minimal code change in the controller

    public interface ICommandDispatcher
    {
        Task HandleAsync<T>(T command) where T:ICommand;
    }
    

And now for the implementation, we'll implements service resolve and invocation part. 

    public class CommandDispatcher : ICommandDispatcher
    {
        private IServiceProvider _serviceProvider;

        public CommandDispatcher(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
        }

        public async Task HandleAsync<T>(T command) where T : ICommand
        {
            var service = this._serviceProvider.GetService(typeof(ICommandHandler<T>)) as ICommandHandler<T>;
            await service.HandleAsync(command);
        }
    }
    

Injected instance of IServiceProvider is handled automatically for us from ASP.NET Core framework, so we can consume it directly in our dispatcher class constructor. Our command and query handlers will be resolved based on setup we already done in previously mentioned article 

    public class Startup
    {
        public void ConfigureServices(this IServiceCollection services)
        {
            services.AddCommandQueryHandlers(typeof(ICommandHandler<>));
            services.AddCommandQueryHandlers(typeof(IQueryHandler<,>));
        }

        private static void AddCommandQueryHandlers(this IServiceCollection services, Type handlerInterface)
        {
            var handlers = typeof(Startup).Assembly.GetTypes()
                .Where(t => t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == handlerInterface)
            );

            foreach (var handler in handlers)
            {
                services.AddScoped(handler.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == handlerInterface), handler);
            }
        }
    }
    

Before we add this dispatcher class to Startup.cs and use it in the controller constructor, let's see how will we create a dispatcher for query handlers.

Query dispatcher

We have our command dispatcher which we'll use instead of command handler instance in the controller constructor. Now for queries, since they have both input class instance which is IQuery class implementation and output which can be nay type we'll have a bit more complex interface for the query dispatcher.

    public interface IQueryDispatcher
    {
        Task<TResult> HandleAsync<TQuery,TResult>(TQuery query) where TQuery : IQuery<TResult>;
    }
    

And for the implementation, we'll rely again to our dependency injection setup done by extension method AddCommandQueryHandlers in the Startup class.

    public class QueryDispatcher : IQueryDispatcher
    {
        private IServiceProvider _serviceProvider;

        public QueryDispatcher(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
        }

        public async Task<TResult> HandleAsync<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>
        {
            var service = this._serviceProvider.GetService(typeof(IQueryHandler<TQuery,TResult>)) as IQueryHandler<TQuery,TResult>;
            return await service.HandleAsync(query);
        }
    }
    

Before we remove all command and query handlers parameters from the controller constructor, we need to register our dispatcher to the IOC container in Startup.cs

    public class Startup
    {
        public void ConfigureServices(this IServiceCollection services)
        {
            services.AddCommandQueryHandlers(typeof(ICommandHandler<>));
            services.AddCommandQueryHandlers(typeof(IQueryHandler<,>));

            services.AddScoped<ICommandDispatcher, CommandDispatcher>();
            services.AddScoped<IQueryDispatcher, QueryDispatcher>();
        }

        private static void AddCommandQueryHandlers(this IServiceCollection services, Type handlerInterface)
        {
            var handlers = typeof(Startup).Assembly.GetTypes()
                .Where(t => t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == handlerInterface)
            );

            foreach (var handler in handlers)
            {
                services.AddScoped(handler.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == handlerInterface), handler);
            }
        }
    }
    

Using dispatchers in controllers

Finally, once we have our command and query dispatchers registered in IOC container, we need only to reference them in the constructor of ASP.NET Core controllers

   public class ProductsController : ControllerBase
    {
        private readonly ICommandDispatcher _commandDispatcher;
        private readonly IQueryDispatcher _queryDispatcher;

        public ProductsController(
            ICommandDispatcher commandDispatcher,
            IQueryDispatcher queryDispatcher
            )
        {
            this._commandDispatcher = commandDispatcher;
            this._queryDispatcher = queryDispatcher;
        }
	}
    

When we need to invoke handler for command, we just need to call HandleAsync method on command dispatcher instance and pass the instance of ICommand implementation. In case of query, we need to invoke HandleAsync on the query dispatcher instance, but instead of only one generic type, we need to provide both IQuery implementation and return result we are expecting in order for dispatcher to resolve it.

This is how handling of command and query would look like now in the controller methods

        [HttpGet]
        public async Task<IActionResult> Get([FromQuery]ProductBrowseQuery productQuery)
        {
            return Ok(await this._queryDispatcher.HandleAsync<ProductBrowseQuery, IEnumerable<ProductView>>(productQuery));
        }
		
        [HttpPost]
        public async Task<IActionResult> Post([FromBody] CreateProductCommand createProductCommand)
        {
            await this._commandDispatcher.HandleAsync<CreateProductCommand>(createProductCommand);
            return CreatedAtRoute(routeName: "Product", routeValues: new { id = createProductCommand.Id }, value: null);
        }

    

By reducing the number of parameters in constructor, we are reducing the number of potential places for bugs to occur and we make our unit tests much cleaner.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article