我正在努力解决我们目前在域事件处理程序中使用 IBus 接口 (NServiceBus v5) 将命令发送到后端服务进行处理的情况。使用 IBus,无论是什么触发了事件,无论是在接收 Web API 请求时还是作为 NServiceBus 处理程序(公共域模型)的一部分,都可以发送这些命令。但是,在 NServiceBus v6 中,随着转向特定于上下文的接口、IEndpointInstance 或 IMessageHandlerContext,我的域事件处理程序现在似乎需要变得上下文感知。而且,看起来 IMessageHandlerContext 只能通过方法注入获得,所以我可能不得不在整个调用堆栈中都使用这个参数?
是否有一些我没有看到的方法可以让我的域事件处理程序上下文不知道?或者我是否遵循了一些通过这种代码气味暴露出来的不良做法?
编辑
这是将场景简化为最相关部分的尝试。领域模型中有一个订单,其状态可能会发生变化。当订单状态发生变化时,我们会通过发布者触发 StatusChanged 域事件。此特定域事件的订阅者写出状态更改的记录,并发送 NServiceBus 命令以传达此状态 - 此特定命令的处理程序将遵循一些关于是否发送电子邮件、SMS 消息等的进一步逻辑.,我认为不相关的细节。
订购领域对象
public class Order
{
private OrderStatusCode _statusCode;
public OrderStatusCode StatusCode
{
get { return _statusCode; }
private set { _statusCode = value; }
}
public void ChangeStatus(OrderStatusCode status)
{
Status = status;
Publish(new StatusChanged(CreateOrderSnapshot(), status));
}
protected void Publish<T>(T @event) where T : IDomainEvent
{
DomainEventPublisher.Instance.Publish(@event);
}
}
领域事件发布者
public class DomainEventPublisher : IDomainEventPublisher
{
private static IDomainEventPublisher _instance;
public static IDomainEventPublisher Instance
{
get { return _instance ?? (_instance = new DomainEventPublisher()); }
}
public ISubscriptionService SubscriptionService { get; set; }
public void Publish<T>(T @event) where T : IDomainEvent
{
if (SubscriptionService == null) return;
var subscriptions = SubscriptionService.GetSubscriptions<T>();
subscriptions.ToList().ForEach(x => PublishToConsumer(x, @event).GetAwaiter().GetResult());
}
private static async Task PublishToConsumer<T>(IEventSubscriber<T> x, T eventMessage) where T : IDomainEvent
{
await x.HandleEvent(eventMessage);
}
}
状态更改域事件处理程序
public class StatusChangedHandler : IEventSubscriber<StatusChanged>
{
private readonly IBus _bus;
private readonly IOrdersRepository _ordersRepository;
public StatusChangedHandler(IBus bus, IOrdersRepository ordersRepository)
{
_bus = bus;
_ordersRepository = ordersRepository;
}
public async Task HandleEvent(StatusChanged @event)
{
var statusTrailEntry = new OrderStatusTrailEntry(@event.OrderSnapshot, @event.Status);
var txOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
using (
var scope = new TransactionScope(TransactionScopeOption.Required, txOptions))
{
await _ordersRepository.SaveStatusTrail(statusTrailEntry);
if (communicateStatus)
{
_bus.Send(new SendCommunicationCommand(@event.OrderSnapshot, @event.Status));
}
scope.Complete();
}
}
}
事情是,到目前为止,上面的示例代码都不需要知道状态是由于通过 Web API 请求进入的请求还是由于在 NServiceBus 消息的上下文中更改状态而改变的处理程序(在 Windows 服务中)- IBus 接口不是特定于上下文的。但是随着 NServiceBus v6 中 IEndpointInstance 和 IMessageHandlerContext 的区别,我不觉得我有同样的灵活性。
如果我理解正确,我可以在我的容器中注册 IEndpointInstance 并注入到 EventSubscriber,所以我会在 Web API 调用的情况下被覆盖,但我还需要添加一个 IMessageHandlerContext 作为参数如果状态恰好在消息处理程序的上下文中更改,则可以选择通过调用堆栈从 ChangeStatus 传递到发布者,最后传递到域事件订阅者。在整个调用堆栈中添加这个参数真的感觉不对。