4th Street Bar Hive-Bar

Hive-Bar Powered by Hive beta-fdb5b5b

Community post

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

25 upvotes $3.37

Replies (1)

Review before signing

Posting as . Signing with . Keychain permission: Posting. Hive Keychain will ask you to approve this action next.


  
Technical details

Operation fingerprint: