イベントを再生するときにCRQSの副作用を処理するにはどうすればよいですか?


10

CQRSではバグを修正するのは簡単で、イベントを再デプロイして再生するだけだと言われています。

ただし、イベントを再生するだけでアイテムが2回出荷される場合、イベントの1つが原因で、制御できない外部システムが顧客に「アイテムを出荷する」原因となる場合はどうでしょうか。

それをどのように解決しますか?

回答:


6

読み取りモデルの状態を変更するイベントと、外部システムの状態を(場合によっては)変更するイベントを明確に分離する必要があります。両方の状態を一緒に変更する「混合イベント」がないことを確認してください。このようにして、外部システムのイベントが再度発生しない特定の「再生モード」でイベントを再生できます。このモードでは、外部システムによって最初に開始されたイベントも「シミュレート」します(今では代わりに再生キューからイベントを取得します)。

忘れないでください。再デプロイのステップは、実際には、読み取りモデルの状態を以前の時点にリセットすることを意味します。これはおそらく、外部システムの状態に対して実行できる(または実行する必要がある)ことではありません。


「忘れないでください。再デプロイのステップは、実際には読み取りモデルの状態を以前の時点にリセットすることを意味します。これは、おそらく外部システムの状態に対して実行できる(または実行する必要がある)ことではありません。」->しかし、リプレイに、出荷などの失敗した外部システムコールを再試行させたい場合はどうなりますか?この場合、再生を再デプロイすると、読み取りモデルの状態がリセットされるだけでなく、外部イベントも発生します。これは公平に聞こえますか、それとも何か不足していますか?
Jas

2
@Jas:失敗した外部システムコールを再試行するために「リプレイ」を悪用したくない。「再生」を使用して、独自のシステムの読み取りモデルを以前と同じ状態で取得します。つまり、配送リクエストが失敗した場合、システムはこの失敗について事前に通知され、その状態のどこかにその情報を保存していました。リプレイは、この情報が「redeploy&replay」の後もそこにあることを確認します。したがって、再生後、システムは「障害発生時に出荷を再試行する」戦略を適用する可能性があります(CQRSとは何の関係もありません。堅牢な注文システムには、このような戦略が必要です)。
ドクブラウン

興味深いことに、これは私がやろうとしていたことであり、これに「パターン」があるかどうか疑問に思っていたので、ホイールを再発明しないでください!
Jas

3

マーティンファウラーのイベントソーシング記事から:

イベントソーシングの基本的な考え方は、アプリケーションの状態に対するすべての変更がイベントオブジェクトに確実にキャプチャされ、これらのイベントオブジェクト自体が、アプリケーションの状態自体と同じ存続期間に適用された順序で格納されるというものです。

したがって、システムの状態を特定の瞬間に復元する必要がある場合は、その瞬間まで、イベントハンドラーではなく、保存された状態を再生します。

とは言っても、状態データのみを操作する場合は、外部システムに影響はありません。イベントストアにトリガーまたはウォッチャーがない場合は、復元中はトリガーまたはウォッチャーを無効にする必要があります。外部システムを制御できないと言っているので、外部システムにどのような副作用があるかわからないため、公開されたAPIを使用して状態を復元しようとする試みはありません。復元によってシステムが中間状態になる場合(たとえば、外部システムでの操作の失敗が原因)、これはイベント再生の責任の範囲内にはなりません。


2

ただし、イベントを再生するだけでアイテムが2回出荷される場合、イベントの1つが原因で、制御できない外部システムが顧客に「アイテムを出荷する」原因となる場合はどうでしょうか。

特定の例を選択するために、副作用に対する「少なくとも1回」のアプローチがどのように機能するかを考えてみましょう。

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

したがって、ドメインモデルは実行する必要があることを追跡します。しかし、実際の処理はアプリケーションに任せます

コマンドを実行するコンテキストでは、基本的な考え方は同じに見えます。実際の副作用は、モデルを更新するトランザクションの外部で発生します。

したがって、モデルの単体テストは次のようになります。

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

ここでの主なポイントは

  • モデルを更新しても副作用はありません。実際の副作用は、モデルを更新するトランザクションの外部で発生します。
  • 副作用の結果を説明するイベントが戻ってくる必要があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.