2

私のドメインでは、複数の集計やその他のサービスを含む高レベルの動作を調整するためにサービスが使用されています。たとえば、注文をキャンセルするときに次の手順を実行する必要がある注文管理システムを考えてみましょう。

  1. 注文の状態を「キャンセル済み」に変更します
  2. 保留中のクレジット カード取引を取り消す
  3. 注文がキャンセルされた理由の説明を含む監査エントリを追加します
  4. Order への変更をデータ ストアに永続化する
  5. OrderCanceledEvent を発生させます (メッセージとして)

これをコーディングするのは非常に簡単ですが、実装する必要がある追加の懸念事項がいくつかあります。

  • 注文が「キャンセル可能」でない場合、どのアクションも実行できません
  • 現在のユーザーが注文をキャンセルする権限を持っていない場合、どのアクションも実行できません
  • クレジットカード取引を取り消すことができない場合、操作全体が失敗し、注文は元の状態のままになるはずです
  • 監査エントリの追加に失敗しても、操作は中止されません
  • 注文の永続化に失敗すると、操作が中止され、注文は元の状態のままになります。

操作は、手順 1、2、および 4 が成功した場合にのみ成功します。そのため、これらの手順をイベント ハンドラーとして実装することはできません。

使用中の永続化メカニズムに関する問題はさておき (これが現状です)、注文の検証、エラー、および状態の管理を適切に処理できるように、サービスを実装する最善の方法を理解するのに役立ちますか?

(私は Event Sourcing を使用していないことに注意してください。したがって、OrderCanceledEvent によってスローされないようにしてください。)

4

1 に答える 1

1

イベントハンドラーでこれを解決する1つの方法は、sagaを使用することです。ワークフローは次のようになります。

  • CancelOrderコマンドを受信すると、aOrderCancellationSagaが開始され、注文がCancelling状態になります。
  • ペイメントゲートウェイからの返金が確認されると、サガが完了し、注文はキャンセルされた状態になり、続行されます。この時点で、同じトランザクション内でOrderCancelledイベントが発生します。
  • 支払いゲートウェイとのやり取りが失敗したり拒否されたりした場合、注文を以前の状態に戻すか、ある種のエラー状態にすることができます。

このシナリオでは、監査はどの段階でも実行できます。また、許可と、最初に注文をキャンセルできるかどうかは、サガを開始する前に、またはサガを開始する最初のステップとして確認する必要があります。

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 に答える