イベントソーシングの副作用に対処するにはどうすればよいですか?


14

奇妙なパターンが検出された場合に電子メールでユーザーに警告する金融アプリケーション用の小さなセキュリティサブシステムを実装すると仮定します。この例では、パターンは図のように3つのトランザクションで構成されます。セキュリティサブシステムは、メインシステムからキューからイベントを読み取ることができます。

私が取得したいのは、パターンの現在の状態をモデル化する中間表現なしで、システムで発生するイベントの直接的な結果であるアラートです。

  1. 監視が有効になりました
  2. 処理されたトランザクション
  3. 処理されたトランザクション
  4. 処理されたトランザクション
  5. アラートがトリガーされました(id:123)
  6. 送信されたアラートの電子メール(ID:123)
  7. 処理されたトランザクション

これを念頭に置いて、明確な答えのない質問がありますが、イベントソーシングはここで非常にうまく適用できると思いました。この例でトリガーされたアラートには、明らかな副作用があり、電子メールを送信する必要があります。これは、一度しか発生しない状況です。したがって、集合体のすべてのイベントを再生するときに発生することはありません。

ある程度、CQRS /イベントソーシングの文献で何度も見たクエリ側で生成された実体化と同様に送信する必要がある電子メールが表示されますが、それほど微妙な違いはありません。

この文献では、クエリ側は、すべてのイベントを再度読み取る特定の時点で状態の具体化を生成できるイベントハンドラから構築されています。ただし、この場合、前に説明した理由により、そのように正確に実行することはできません。すべての状態が一時的であるという考えはここでそれほど適切ではありません。アラートがどこかに送信されたという事実を記録する必要があります。

私にとって簡単な解決策は、以前にトリガーされたアラートの記録を保持する別のテーブルまたは構造を持つことです。IDがあるため、同じIDのアラートが以前に発行されたかどうかを確認できます。この情報があると、SendAlertCommandはべき等になります。複数のコマンドを発行できますが、副作用は一度しか発生しません。

その解決策を念頭に置いても、これがこの問題のこのアーキテクチャに何か問題があるというヒントかどうかはわかりません。

  • 私のアプローチは正しいですか?
  • これに関する詳細情報を見つけることができる場所はありますか?

これについての詳細情報を見つけることができなかったことは奇妙です。たぶん私は間違った表現を使っていたのかもしれません。

どうもありがとうございます!

回答:


12

イベントソーシングの副作用に対処するにはどうすればよいですか?

短いバージョン:ドメインモデルは副作用を実行しません。それは追跡し、それらを。副作用は、境界に接続するポートを使用して実行されます。電子メールが送信されると、ドメインモデルに確認応答を送り返します。

これは、イベントストリームを更新するトランザクションの外部に電子メールが送信されることを意味します。

正確には、外では、好みの問題です。

概念的には、次のようなイベントのストリームがあります

EmailPrepared(id:123)
EmailPrepared(id:456)
EmailPrepared(id:789)
EmailDelivered(id:456)
EmailDelivered(id:789)

そして、このストリームからフォールドを作成できます

{
    deliveredMail : [ 456, 789 ],
    undeliveredMail : [123]
}

フォールドは、どのメールが承認されていないかを示しているので、もう一度送信します。

undeliveredMail.each ( mail -> {
    send(mail);
    dispatch( new EmailDelivered.from(mail) );
}     

事実上、これは2フェーズコミットです。実世界でSMTPを変更してから、モデルを更新しています。

上記のパターンは、少なくとも1回の配信モデルを提供します。一度だけ必要な場合は、向きを変えることができます

undeliveredMail.each ( mail -> {
    commit( new EmailDelivered.from(mail) );
    send(mail);
}     

EmailPreparedを永続化することと実際にメールを送信することの間には、トランザクションの障壁があります。また、電子メールの送信とEmailDeliveredの耐久性の間にトランザクションバリアがあります。

分散トランザクションを使用したUdi Dahanの信頼性のあるメッセージングは、出発点として適しています。


2

「状態変更イベント」を「アクション」から分離する必要があります

状態変更イベントは、オブジェクトの状態を変更するイベントです。これらは、保存して再生するものです。

アンアクションは、オブジェクトが他のものにし、何かです。これらはイベントソーシングの一部として保存されません。

これを行う1つの方法は、イベントハンドラーを使用することです。これは、アクションを実行するかどうかに応じて接続します。

public class Monitor
{
    public EventHander SoundAlarm;
    public void MonitorEvent(Event e)
    {
        this.eventcount ++;
        if(this.eventcount > 10)
        {
             this.state = "ALARM!";
             if(SoundAlarm != null) { SoundAlarm();}
        }
    }
}

今、私の監視サービスで私は持つことができます

public void MonitorServer()
{
    var m = new Monitor(events); //11 events
    //alarm has not been sounded because the event handler wasn't wired up
    //but the internal state is correctly set to "ALARM!"
    m.SoundAlarm += this.SendAlarmEmail;
    m.MonitorEvent(e); //email is sent
}

送信した電子メールを記録する必要がある場合は、SendAlarmEmailの一部として記録できます。しかし、それらはイベントソーシングの意味でのイベントではありません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.