奇妙なパターンが検出された場合に電子メールでユーザーに警告する金融アプリケーション用の小さなセキュリティサブシステムを実装するとします。この例では、パターンは3つのトランザクションで構成されます。セキュリティサブシステムは、メインシステムのイベントをキューから読み取ることができます。
取得したいのは、パターンの現在の状態をモデル化する中間表現がない、システムで発生するイベントの直接的な結果であるアラートです。
これを念頭に置いて、明確な答えがない質問がありますが、イベントソーシングはここで非常にうまく適用できると思いました。この例でトリガーされたアラートには明確な副作用があり、電子メールを送信する必要があり、状況は1回だけ発生します。したがって、アグリゲートのすべてのイベントを再生するときに発生することはありません。
CQRS /イベントソーシングの文献で何度も見たクエリ側で生成された実体化と同様に、送信する必要がある電子メールがある程度見られますが、それほど微妙な違いはありません。
この文献では、クエリ側は、すべてのイベントを再度読み取る特定の時点での状態の具体化を生成できるイベントハンドラーから構築されています。ただし、この場合は、前に説明した理由により、そのように正確に実行することはできません。 すべての状態が一時的であるという考えは、ここではあまり当てはまりません。アラートがどこかに送信されたという事実を記録する必要があります。
私にとっての簡単な解決策は、以前にトリガーされたアラートの記録を保持する別のテーブルまたは構造を持つことです。 IDがあるため、同じIDのアラートが以前に発行されたかどうかを確認できます。この情報があると、SendAlertCommandはべき等になります。複数のコマンドを発行できますが、副作用は1回だけ発生します。
その解決策を念頭に置いても、これがこの問題のこのアーキテクチャに問題があることのヒントであるかどうかはわかりません。
私がこれについてこれ以上の情報を見つけることができなかったのは奇妙です。多分私は間違った文言を使用しています。
どうもありがとうございます!
イベントソーシングの副作用に対処するにはどうすればよいですか?
ショートバージョン:ドメインモデルは副作用を実行しません。それtracksそれら。副作用は、境界に接続するポートを使用して実行されます。電子メールが送信されると、確認応答がドメインモデルに送信されます。
これは、メールが送信されることを意味します外部イベントストリームを更新するトランザクション。
正確には、どこで、外で、好みの問題です。
つまり、概念的には、次のような一連のイベントがあります。
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を耐久性のあるものにすることと実際にメールを送信することの間にトランザクションの障壁があります。また、Eメールの送信とEmailDeliveredの永続化との間にはトランザクションの障壁もあります。
Udi Dahanの 分散トランザクションを使用した信頼性の高いメッセージング は、出発点として適しています。
「状態変更イベント」を「アクション」から分離する必要があります
状態変更イベントは、オブジェクトの状態を変更するイベントです。これらは、保存して再生するものです。
Actionは、オブジェクトが他のことに対して行うことです。これらはイベントソーシングの一部として保存されません。
これを行う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の一部としてログに記録できます。しかし、それらはイベントソーシングの意味でのイベントではありません