【问题标题】:Transaction scoping for synchronous command/event handlers (in NServiceBus)同步命令/事件处理程序的事务范围(在 NServiceBus 中)
【发布时间】:2014-04-09 21:24:52
【问题描述】:

假设我们有以下业务规则:

  1. 取消订单后,所有未发货的货物都应 取消。
  2. 个别货件可能因其他原因被取消。

如果我们同步做这些事情,我对事务范围有疑问:

  1. 我听说过 Aggregate == “事务边界”,但在这种情况下,请求范围 DbContext 是否更有益,以便所有同步处理程序在同一个事务中执行?如果后 2 个处理程序发生故障,它将提供更高的一致性。
  2. 如果#1 不是一个好主意,为什么?我们是否只是说“每个处理程序在其自己的事务中,如果在其中出现问题,你就靠自己了”?
  3. 如果 #1 是个好主意,我该如何使用 NServiceBus 的Bus.InMemory 做到这一点? The documentation对这个问题不是很清楚。

代码如下:

public class CancelOrder : ICommand 
{
    public Guid Id { get; set; }
}

public class OrderCancelled : IEvent
{
    public Guid Id { get; set; }
}

public class CancelShipment : ICommand
{
    public Guid Id { get; set; }
}

public class CancelOrderHandler : IHandleMessages<CancelOrder>
{
    private DbContext _dbContext;
    private IBus _bus;

    public void Handle(CancelOrder message) 
    {
        var order = _dbContext.Orders.Single(x => x.Id == message.Id);

        // do some validation here

        order.Status = OrderStatus.Cancelled;

        // unsure about whether to call _dbContext.SaveChanges() or not

        _bus.Publish(new OrderCancelled {
            Id = message.Id
        });
    }
}

public class OrderCancelledHandler : IHandleMessages<OrderCancelled>
{
    private DbContext _dbContext;
    private IBus _bus;

    public void Handle(CancelOrder message) 
    {
        var commands = _dbContext.Shipments
            .Where(x => x.OrderId == message.Id && x.Status != ShipmentStatus.Shipped)
            .Select(x => new CancelShipment {
                Id = x.Id
            });

        _bus.Send(commands);
    }
}

public CancelShipmentHandler : IHandleMessages<CancelShipment>
{
    private DbContext _dbContext;

    public void Handle(CancelShipmentHandler message) 
    {
        // Similar to CancelOrder, only with shipments.
    }
}

【问题讨论】:

    标签: transactions domain-driven-design nservicebus


    【解决方案1】:

    这里的答案取决于业务需求——例如,如果我们无法取消其中一件货物,是否意味着我们应该回滚取消订单?

    我的感觉是答案可能是否定的。因此,您使用的消息驱动模型似乎比在单个处理程序中同步执行所有操作更合适。

    我对您的代码的一个小问题是您在 OrderCancelledHandler 中发布命令。您可能应该使用 Bus.Send 代替。

    此外,通过执行单个 Bus.Publish/Bus.Send,您表明整个命令集的处理应该是一个事务。我不认为你想要那个。相反,我建议循环执行命令并分别为每个命令执行 Bus.Send。

    【讨论】:

    • 事件处理程序中的 Bus.Send 是一个错字,现已更正。另外,感谢您对交易范围的澄清。我还假设命令处理程序-> 事件处理程序-> 命令处理程序的流程在这里是合适的,因为您只提出了一个问题。非常感谢您的反馈,Udi。
    • 这也隐含地回答了我的一个大问题,即 NSB 始终对每个消息有效负载(即通过对 Bus.Send/Publish 的单个调用传递的任何内容)进行限定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    • 2011-07-05
    • 2011-06-28
    相关资源
    最近更新 更多