Preparation for plug-and-play architecture in Asp.NET 5

As a continuation of the "plug-and-play architecture" series we need to prepare a small Mediator pattern-based CQRS architecture.

First we need to wrap any result that a command or a query can return

namespace PlugAndPlayExample.Services.Infrastructure
{
    public class Response
    {
        public Response()
        {
            Exceptions = new List<Exception>();
        }

        public List<Exception> Exceptions { get; set; }
    }

    public class Response<T> : Response
    {
        public T Result { get; set; }

        public static implicit operator Response<T>(T result)
        {
            return new Response<T> { Result = result };
        }
    }
}

Second, we need to create the command and query handler interfaces

namespace PlugAndPlayExample.Services.Infrastructure
{
    public interface ICommand
    {
    }

    public interface ICommandHandler<TCommand, TResult> 
        where TCommand: ICommand 
        where TResult : Response
    {
        TResult Handle(TCommand command);
    }

    public interface IQuery
    {
    }

    public interface IQueryHandler<TQuery, TResult>
        where TQuery : IQuery
        where TResult : Response
    {
        TResult Handle(TQuery query);
    }
}

We need a mediator that will dispatch these requests to their appropriate handlers

namespace PlugAndPlayExample.Services.Infrastructure
{
    public class Mediator
    {
        private readonly IServiceProvider serviceProvider;

        public Mediator(IServiceProvider serviceProvider)
        {
            this.serviceProvider = serviceProvider;
        }

        public TResult Dispatch<TCommand, TResult>(TCommand command)
            where TCommand : ICommand
            where TResult : Response
        {
            var commandHandler = serviceProvider.GetService(typeof(ICommandHandler<TCommand, TResult>)) as ICommandHandler<TCommand, TResult>;
            return commandHandler.Handle(command);
        }

        public TResult Get<TQuery, TResult>(TQuery query)
            where TQuery : IQuery
            where TResult : Response
        {
            var queryHandler = serviceProvider.GetService(typeof(IQueryHandler<TQuery, TResult>)) as IQueryHandler<TQuery, TResult>;
            return queryHandler.Handle(query);
        }
    }
}

And register everything in the startup

namespace PlugAndPlayExample.Configuration
{
    public static class RegisterServicesExtension
    {
        public static IServiceCollection RegisterServices(this IServiceCollection services)
        {
            services.AddSingleton<Mediator>();

            var queryHandlerType = typeof(IQueryHandler<,>);
            var commandHandlerType = typeof(ICommandHandler<,>);

            RegisterOfType(services, queryHandlerType);
            RegisterOfType(services, commandHandlerType);

            return services;
        }

        private static void RegisterOfType(IServiceCollection services, Type type)
        {
            var exportedTypes = typeof(Mediator).Assembly.GetExportedTypes();

            var result = exportedTypes.Where(x => x
                .GetInterfaces()
                    .Any(i => i.IsGenericType
                            && i.GetGenericTypeDefinition() == type)
                            && x.IsClass
                            && !x.IsAbstract)
                .ToList();

            result.ForEach(handler =>
            {
                var handlerType = handler;
                var serviceType = handler
                    .GetInterfaces()
                    .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == type);

                services.AddTransient(serviceType, handlerType);
            });
        }
    }
}
namespace PlugAndPlayExample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.RegisterServices();
        }

        // Configure code ...
    }
}

With there few infrastructure preparations we are ready to start implementing the plug-and-play architecture.

Until next time...

Happy coding,
DotNetGuru

Sort:  

Congratulations @dotnetguru! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You distributed more than 10 upvotes.
Your next target is to reach 50 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Check out the last post from @hivebuzz:

Hive Power Up Month - Feedback from day 3
Happy New Year - Feedback from the first Hive Power Up Day of 2022
Support the HiveBuzz project. Vote for our proposal!