イベント駆動アーキテクチャで重要な設計パターン
はじめに
イベント駆動アーキテクチャ(Event-Driven Architecture, EDA)を設計する場合は、局所的にアーキテクチャの適用というケースは少なく、システム全体を巻き込んだものになることが多いと思います。
システム全体を巻き込んだアーキテクチャ設計となると、さまざま処理パターン、処理方式を組み合わせることになるため、とても難易度が高いです。
この記事ではイベント駆動アーキテクチャを設計する際に知っておくと良いと思う設計パターンを紹介します。
Fanout(ファンアウト)
イベント駆動アーキテクチャを採用したいケースでは、システム内に発生したイベントをトリガーとして、複数の処理を実行したいもしくは今後の拡張性として複数の処理を実行できるようにしたいケースが多いと思いますが、そのような場合にファンアウト(以降 Fanout)構成を取っておくと良いと思います。
Fanoutはイベントブローカーに送信されたイベントを複数のコンシューマーがサブスクライブできるようにするアーキテクチャパターンです。こうすることによって同じイベントに対して2つ処理を行いたい場合に相互に干渉することなく処理を追加できます。
Outboxパターン
ありがちな失敗としてデータベースの更新とイベントの発行を同一トランザクションで実行してしまい、イベントの発行に失敗した場合にデータベースの更新がロールバックされてしまうケースがあります。データ不整合が発生するため障害テストなどを実施していれば気づくことができるかもしれませんが、テスト時に検知できなかった場合は、運用中にデータ不整合が発生してしまう可能性があり大変危険です。AWSのドキュメントも参照してください。
OutboxパターンはCDC(Change Data Capture)とも関連が深いです。
CDCはデータベースへの変更情報をStreamなどで連携するための仕組みです。AWSですと DynamoDB
には DynamoDB Stream
という機能がありCDCを簡単に実現できます。
またNoSQLだけではなく、RDBの更新をトリガーとしてイベントを発行する場合に有効です。
RDBの更新とイベント発行を同一トランザクションで実行することはできませんが、Outboxテーブルにイベントを保存することで、RDBの更新とイベント発行を分離し、データの整合性を保つことができます。
Sagaパターン
分散トランザクションを実現するためのパターンです。イベント駆動アーキテクチャでは、複数のサービスが連携して1つのビジネスプロセスを実行することが多く、その際に各サービスが独自のデータストアを持つ場合、データの一貫性を確保することが難しくなります。Sagaパターンは、各サービスが独自にトランザクションを管理し、必要に応じて補償トランザクションを実行することで、データの一貫性を確保します。
実際に決済系処理などを実装する場合は、Sagaパターンで補償しているから大丈夫だ。と思わずに、補償処理が失敗した場合のリトライや手動対応なども考慮する必要があります。
Idempotency(冪等性)
冪等性とは、同じ操作を何度実行しても結果が変わらない性質のことを指します。
設計パターンというよりは性質ですが、イベント駆動アーキテクチャを設計する際に非常に重要な概念です。イベント駆動アーキテクチャでは、同じイベントが複数回処理される可能性があるため、各処理が冪等であることが重要であり、これによってイベントの重複処理や再試行が発生しても、システムの状態が一貫して保たれます。
前述したSagaパターンの処理X・処理X補償などは冪等である必要があります。
PowerTools for AWS Lambda の Idempotency Utility などがとても参考になります。
非同期要求-応答パターン
イベント駆動アーキテクチャに限ったことではありませんが、
オンラインで重たい処理(処理時間が長い)を実行する場合は、極力非同期で実行することを検討した方が良いです。
非同期で実行することで、ユーザーに対して即時に応答を返すことができ、ユーザー体験を向上させることができますし、バックエンドとしてもWait状態でリソースがロックされることがありませんので、システム全体のスループットも向上します。例えば、ユーザーがWebアプリケーションで重たい処理を実行した場合、即時に「処理を受け付けました」という応答を返し、バックエンドで非同期に処理を実行します。
Microsoftのクラウドデザインパターンにも同様のパターンが紹介されています。
おわりに
イベント駆動アーキテクチャを設計する際に知っておくと良いと思う設計パターンを紹介しました。
もちろんこれら以外にも設計パターンは存在しますし、実際のシステム設計では、これらのパターンを組み合わせて使用することが多いです。
また、これらのパターンを適用する際には、システムの要件や制約を十分に考慮し、適切なパターンを選択することが重要です。
Discussion