2

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

  1. 将订单状态更改为“已取消”
  2. 撤销任何待处理的信用卡交易
  3. 添加包含订单取消原因说明的审核条目
  4. 将订单更改持久保存到数据存储
  5. 引发 OrderCanceledEvent(作为消息)

对此进行编码非常简单,除了我必须实现一些额外的问题:

  • 如果订单未处于“可取消”状态,我将无法执行任何操作
  • 如果当前用户无权取消订单,我将无法执行任何操作
  • 如果我无法撤销信用卡交易,那么整个操作将失败,订单应保持其原始状态
  • 未能添加审计条目不会中止操作
  • 未能坚持订单应中止操作,订单应保持其原始状态

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

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

(我应该注意我没有使用事件溯源,所以不要让 OrderCanceledEvent 丢给你。)

4

1 回答 1

1

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

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

在这种情况下,审计可以在任何阶段进行。此外,在开始 saga 之前或作为开始 saga 的第一步,应首先验证权限以及是否可以取消订单。

带有 C# 和NServiceBus传奇的粗略示例:

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();
  }

}
于 2012-11-26T22:53:38.260 回答