Autofac解决了CQRS CommandDispatcher中的依赖关系
本文关键字:依赖 关系 CommandDispatcher 解决 CQRS Autofac | 更新日期: 2023-09-27 18:28:04
我正在尝试实现一个简单的CQRS应用程序示例。
这是我的"命令"部分的结构:
public interface ICommand
{
}
//base interface for command handlers
interface ICommandHandler<in TCommand> where TCommand: ICommand
{
void Execute(TCommand command);
}
// example of the command
public class SimpleCommand: ICommand
{
//some properties
}
// example of the SimpleCommand command handler
public class SimpleCommandHandler: ICommandHandler<SimpleCommand>
{
public void Execute(SimpleCommand command)
{
//some logic
}
}
这是接口ICommandDipatcher
。它将一个命令分派给它的处理程序。
public interface ICommandDispatcher
{
void Dispatch<TCommand>(TCommand command) where TCommand : ICommand;
}
这是ICommandDispatcher
的默认实现,主要问题是通过Autofac
根据命令的类型获得必要的命令处理程序。
public class DefaultCommandDispatcher : ICommandDispatcher
{
public void Dispatch<TCommand>(TCommand command) where TCommand : ICommand
{
//How to resolve/get object of the neseccary command handler
//by the type of command (TCommand)
handler.Execute(command);
}
}
在这种情况下,通过Autofac通过命令类型解决ICommandHanler
实现的最佳方法是什么?
谢谢!
使用Autofac,您需要将IComponentContext
注入调度器。通过这种方式,您可以回调到容器中以解析所需的命令处理程序:
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);
}
}
您可以按以下方式注册AutofacCommandDispatcher
:
builder.RegisterType<AutofacCommandDispatcher>().As<ICommandDispatcher>();
您可以一次注册所有的命令处理程序,如下所示:
builder.RegisterAssemblyTypes(myAssembly)
.AsClosedTypesOf(typeof(ICommandHandler<>));
不过有两个音符。首先,您可能将ICommandHandler<T>
定义为反变量(使用in
关键字),因为Resharper是这么说的,但这对命令处理程序来说是个坏主意。命令和命令处理程序之间总是有一对一的映射,但定义in
关键字表明可以有多个实现。
其次,在我看来,拥有一个命令调度器是个坏主意,因为这会掩盖命令处理程序的消费类有太多依赖关系的事实,这表明违反了单一责任原则。此外,使用这样的调度器会推迟对象图的一部分(命令处理程序的一部分)的创建,直到命令实际执行(而不是解析使用者时)。这使得验证容器的配置更加困难。当直接注入命令处理程序时,您可以确信,当可以解析配置中的根类型时,可以解析整个对象图。定义命令很容易,但忘记了创建相应的命令处理程序,因此需要为此添加单元测试,以检查每个命令是否都有相应的处理程序。如果您将调度器全部删除,就可以避免编写这样的测试。
假设您有ConcreteCommand : IComman
和ConcreteCommandHandler : ICommandHandler<ConcreteCommand>
,请使用RegisterType方法,如下所示:
builder.RegisterType<ConcreteCommandHandler>()
.As<ICommandHandler<ConcreteCommand>>();
然后注入你的处理程序:
private ICommandHandler<ConcreteCommand> concreteCommandHandler;
还可以查看自动装配类型注册Autofac功能。
如果您想通过ICommand
实现来解决ICommandHandler
问题,那么工厂注册会有所帮助。注册Func<Type, ICommandHandler>
或定义将解析适当命令处理程序的特殊类。