容错&域服务中的状态管理

时间:2012-11-26 21:15:57

标签: domain-driven-design

在我的域中,服务用于协调涉及多个聚合和/或其他服务的更高级别行为。例如,订单管理系统在取消订单时需要执行以下步骤:

  1. 将订单状态更改为“已取消”
  2. 撤消任何待处理的信用卡交易
  3. 添加一个审核条目,其中包含解释订单被取消的原因
  4. 将订单的更改保留到数据存储
  5. 提升OrderCanceledEvent(作为消息)
  6. 对此进行编码非常简单,除了我必须实现的一些其他问题:

    • 如果订单不在,我无法执行任何操作 “可取消的”州
    • 如果当前用户没有,我无法执行任何操作 取消订单的许可
    • 如果我无法撤销信用卡交易那么整个 操作失败,订单应保持原状 状态
    • 未能添加审核条目不会中止操作
    • 如果未能保留订单,则应中止操作和订单 应该保持原状状态

    仅当步骤1,2和4成功时,操作才会成功。因此,我不能将这些步骤实现为事件处理程序。

    暂停使用持久性机制的任何问题(它就是这样),您能否帮助我了解如何最好地实施我的服务,以便它正确处理验证,错误和管理订单状态?

    (我应该注意到我没有使用Event Sourcing,所以不要让OrderCanceledEvent抛弃你。)

1 个答案:

答案 0 :(得分:1)

使用事件处理程序解决此问题的一种方法是使用saga。工作流程如下:

  • 收到CancelOrder命令后,系统会启动OrderCancellationSaga,将订单置于Cancelling状态。
  • 确认支付网关的退款后,该传奇已完成,订单将被置于取消状态并持续存在。此时,在同一事务中,会引发OrderCancelled事件。
  • 如果与支付网关的交互失败或被拒绝,订单可以恢复到先前状态或置于某种错误状态。

在此方案中,审核可以在任何阶段进行。此外,应该在开始传奇之前验证权限以及是否可以首先取消订单,或者作为启动传奇的第一步。

使用C#和NServiceBus sagas的粗略样本:

class OrderOrderCancellationSaga : Saga<OrderCancellationSagaData>
  ,IAmStartedBy<CancelOrderCommand>,
  ,IHandle<PaymentGatewayInteractionFailedEvent> 
{

  public OrderService OrderService { get; set; }
  public PaymentGateway PaymentGateway { get; set; }

 // correlate saga messages by order ID  
 public override void ConfigureHowToFindSaga()
 {
      ConfigureMapping<PaymentGatewayInteractionFailedEvent>(x => x.OrderId, x =>x.OrderId);
      ConfigureMapping<RefundCompletedEvent>(x => x.OrderId, x => x.OrderId);
 }

  // start cancellation process
  public void Handle(CancelOrderCommand message) 
  {
     // check if cancellation is authorized and valid
     // ....

     // can save prior state here, if needed

     this.Data.OrderId = message.OrderId;
     this.Data.State = "Cancelling";    


     this.Bus.Send(new RefundOrderCommand(...));
  }

  public void Handle(RefundCompletedEvent message)
  { 
     this.Data.State = "Cancelled"; 
     this.OrderService.CompleteCancellation(...);                 
     MarkAsComplete();
  }

  // this handler can be hosted on a different endpoint.
  public void Handle(RefundOrderCommand message)
  { 
     try
     {
        this.PaymentGateway.Refund(...
     }
     catch(Exception ex)
     {
        this.Bus.Reply(new PaymentGatewayInteractionFailedEventmessage(...));
     }
  }

  // this handler can be used to revert whole operation.
  public void Handle(PaymentGatewayInteractionFailedEvent message)
  {
     // or revert to prior state.
     this.Data.Status = "Cancellation Failed";

     // call any application services needed.

     // finishes saga, deleting state
     MarkAsComplete();
  }

}