Search code examples
c#decoratorautofaccqrsmediatr

CQRS Commands/Queries decorators


How can I add custom decorators for IQueryHandler and ICommandHandler?

I'm trying to implement CQRS pattern in simple console app, using Autofac and MediatR library.

The project has the next structure:

enter image description here

I want to make processing of commands and queries in this way:

  • Request Handler Decorators applying
  • Command/Query Handler Decorators applying
  • Command/Query Handler do something
  • Command/Query Handler Decorators finishing
  • Request Handler Decorators finishing

Main File:

internal class Program
{
    static async Task Main(string[] args)
    {
        var container = ConfigureCQRS();
        using var scope = container.BeginLifetimeScope();

        var mediatr = scope.Resolve<IMediator>();

        await mediatr.Send(new MyCommand("Command1"));
        await mediatr.Send(new MyQuery("Query1"));
    }

    private static IContainer ConfigureCQRS()
    {
        var builder = new ContainerBuilder();

        builder.RegisterModule(new MediatRModule(typeof(Program).Assembly));
        builder.RegisterGenericDecorator(typeof(LoggingRequestHandlerDecorator<,>), typeof(IRequestHandler<,>));
        builder.RegisterGenericDecorator(typeof(UnitOfWorkCommandHandlerDecorator<,>), typeof(ICommandHandler<,>));
        builder.RegisterGenericDecorator(typeof(DiagnosticQueryHandlerDecorator<,>), typeof(IQueryHandler<,>));

        return builder.Build();
    }
}

MediatRModule.cs:

internal class MediatRModule : Autofac.Module
{
    private readonly Assembly[] assemblies;

    internal MediatRModule(params Assembly[] assemblies)
    {
        this.assemblies = assemblies;
    }

    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterMediatR(assemblies);

        var openHandlerTypes = new[]
        {
            typeof(IRequestHandler<,>),
            typeof(IRequestExceptionHandler<,,>),
            typeof(IRequestExceptionAction<,>),
            typeof(INotificationHandler<>),
        };

        foreach (var openHandlerType in openHandlerTypes)
        {
            builder.RegisterAssemblyTypes(this.assemblies)
             .AsClosedTypesOf(openHandlerType)
             .AsImplementedInterfaces();
        }

        builder.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        builder.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        builder.RegisterGeneric(typeof(RequestExceptionActionProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        builder.RegisterGeneric(typeof(RequestExceptionProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));

        builder.Register<ServiceFactory>(outerContext =>
        {
            var innerContext = outerContext.Resolve<IComponentContext>();

            return serviceType => innerContext.Resolve(serviceType);
        })
            .InstancePerLifetimeScope();
    }
}

Typical command with decorator:

internal record MyCommand(string Text) : CommandBase<string>;

internal class MyCommandHandler : ICommandHandler<MyCommand, string>
{
    public Task<string> Handle(MyCommand command, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Command result: {command.Text}");
        return Task.FromResult(command.Text);
    }
}

Typical request handler decorator:

internal class LoggingRequestHandlerDecorator<TRequest, TResult> :
    IRequestHandler<TRequest, TResult>
    where TRequest : IRequest<TResult>
{
    private readonly IRequestHandler<TRequest, TResult> _decorated;

    public LoggingRequestHandlerDecorator(IRequestHandler<TRequest, TResult> decorated)
    {
        this._decorated = decorated;
    }

    public async Task<TResult> Handle(TRequest request, CancellationToken cancellationToken)
    {
        Console.WriteLine("LoggingRequestHandlerDecorator start");
        var result = await _decorated.Handle(request, cancellationToken);
        Console.WriteLine("LoggingRequestHandlerDecorator end");
        return result;
    }
}

Typical command handler decorator:

internal class UnitOfWorkCommandHandlerDecorator<TCommand, TResult> :
    ICommandHandler<TCommand, TResult>
    where TCommand : ICommand<TResult>
{
    private readonly ICommandHandler<TCommand, TResult> _decorated;

    public UnitOfWorkCommandHandlerDecorator(ICommandHandler<TCommand, TResult> decorated)
    {
        _decorated = decorated;
    }

    public async Task<TResult> Handle(TCommand command, CancellationToken cancellationToken)
    {
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator start");
        var result = await _decorated.Handle(command, cancellationToken);
        Console.WriteLine("UnitOfWorkCommandHandlerDecorator end");
        return result;
    }
}

Full project published on the github: github.com/LeftTwixWand/CommandQueryDecorators


Solution

  • Ok, I've sole this. Just need to accept IRequestHandler in the constructor of decorators.

    Also, I created an example on GitHub of how you can do it: https://github.com/LeftTwixWand/ModernCQRS

    This repository demonstrates:

    • How to set up the MediatR
    • How to create custom Commands / Queries and its handlers
    • How to add decorators for all types of request handlers
    • How to add decorators only for Command or Query handlers
    • How to use MediatR pipelines

    The sample app output: enter image description here