使用事件处理程序解决此问题的一种方法是使用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();
}
}