Autofac resolve dependency in CQRS CommandDispatcher

后端 未结 2 1726
小蘑菇
小蘑菇 2020-12-31 09:04

I\'m trying to implement a simple CQRS-application example.

This is a structure of my \"Command\" part:

public interface ICommand
{
}

//base interfa         


        
相关标签:
2条回答
  • 2020-12-31 09:39

    With Autofac, you need to inject the IComponentContext into the dispatcher. This way you can call back into the container to resolve the required command handler:

    public class AutofacCommandDispatcher : ICommandDispatcher
    {
        private readonly IComponentContext context;
    
        public AutofacCommandDispatcher(IComponentContext context)
        {
            this.context = context;
        }
    
        public void Dispatch<TCommand>(TCommand command)
        {
            var handler = this.context.Resolve<ICommandHandler<TCommand>>();
    
            void handler.Execute(command);
        }
    }
    

    You can register the AutofacCommandDispatcher as follows:

    builder.RegisterType<AutofacCommandDispatcher>().As<ICommandDispatcher>();
    

    And you can register all your command handlers in one go as follows:

    builder.RegisterAssemblyTypes(myAssembly)
        .AsClosedTypesOf(typeof(ICommandHandler<>));
    

    Two notes though. First of all, you probably defined the ICommandHandler<T> as contravariant (with the in keyword) because Resharper said so, but this is a bad idea for command handlers. There is always a one-to-one mapping between a command and command handler, but defining the in keyword, communicates that there can be multiple implementations.

    Second, in my opinion, having a command dispatcher is a bad idea, because this can hide the fact that the consuming classes of command handlers have too many dependencies, which is an indication of a violation of the Single Responsibility Principle. Further more, using such a dispatcher postpones creation of part of the object graph (the part of the command handler) until the command is actually executed (opposed to when the consumer is resolved). This makes it harder to verify the container's configuration. When command handlers are injected directly, you know for sure that the whole object graph can be resolved when the root types in your configuration can be resolved. It's easy to define a command but forget to create the corresponding command handler, so you will need to add unit tests for this to check if each command has a corresponding handler. You can save yourself from having to write such test if you remove the dispatcher all together.

    0 讨论(0)
  • 2020-12-31 09:45

    Assuming you have ConcreteCommand : IComman and ConcreteCommandHandler : ICommandHandler<ConcreteCommand> use RegisterType method like this:

    builder.RegisterType<ConcreteCommandHandler>()
           .As<ICommandHandler<ConcreteCommand>>();
    

    And then inject your handler:

    private ICommandHandler<ConcreteCommand> concreteCommandHandler;
    

    Also look at the automatic assembly types registration Autofac abilities.

    If you'd like to resolve ICommandHandler by ICommand implementation then factory registration can help. Register Func<Type, ICommandHandler> or define special class that will resolve appropriate command handler.

    0 讨论(0)
提交回复
热议问题