【Event-Driven Architectureへの道】イベント・ドリブンアーキテクチャ入門
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
イベント・ドリブンアーキテクチャ(Event-Driven Architecture)とは?
イベント・ドリブンアーキテクチャ(Event-Driven Architecture:EDA)は、マイクロサービス化されたビジネスロジックを効果的に実装するための一つの強力なアーキテクチャスタイルです。
細かく機能分割されたマイクロサービスがビジネスタスクを実行するためにはサービス間の論理的なコミュニケーションを必要とします。一連のビジネスロジックが持つセマンティックを実現するためには複数のマイクロサービス間で論理的且つ秩序ある会話が必要であり、そのような制御が取れた一連のフロー状に実装する必要があります。即ち、マイクロサービスによるビジネスロジックの実装は、論理的な判断を含む一連のワークフローになることが一般的です。
EDAは、この"コミュニケーション"をビジネスタスクの要求に対して進行させることで、独立したサービスを繋いだビジネスタスクを実行します。各々のマイクロサービスは他のサービスには依存せず、特定のイベントに対し責務を持ち機能を提供します。この結果、マイクロサービス間の疎結合なコミュニケーションが可能になり、スケーラビリティと耐障害性を確保することができます。
マイクロサービス化されたビジネスロジックの実装において、EDAは非常に強力なツールです。
しかしながら、実装の複雑さ、チームの技術スキル、対象となるビジネスロジックやシステムの性質など、他の多くの要因も考慮する必要があります。したがって、EDAが常に"最も"効果的な方法であるとは限らないことも理解が必要です。このシリーズではイベント・ドリブンアーキテクチャのメリット・デメリットに加え、向き・不向きや導入のポイントについても解説していく予定です。
イベントとは?
「イベントは状態の変更を表す示すシグナル」です。
イベントの中にはビジネスの要求(=コマンド)と1対1で対応するような状態変化も含まれます。例としてECサイトの場合、カートに入れた商品に必要な情報(決済情報や住所情報など)を入力し、購入ボタンを押した際にもイベントが発行します。
このような場合「購入」というコマンドと捉えられがちですが、あくまでも「購入要求をした」というイベントとして扱うことが重要です。コマンドは基本一つのサービスに対する命令ですが、イベントは複数のサービスに伝播し各々処理することができます。このような特性は新たなサービスを繋げたり、ビジネス要求に応じてワークフローを更新する場合に非常に有効で、システムを継続的に変化・成長させる面で非常に重要です。
イベントは、アプリケーションの情報やエラーログのように、何が起こったかを記録するものではありますが、異なる部分としては、唯一信頼できる情報源となります。何が起こったのかを正確に示すための情報源が含まれていることが前提となります。
イベント・ドリブンアーキテクチャと従来のアーキテクチャの違い
リクエスト・リプライ型(APIベース)
典型的なAPIベースで構築されたマイクロサービスのアーキテクチャは以下のようなリクエスト・リプライ型になることが一般的です。このアーキテクチャは非常に汎用的で処理フローが記述しやすい一方で以下のようなデメリットがあります。
・接続処理がビジネスロジックに含まれるため、ドメイン間の独立性を十分に担保できない。
・機能追加・機能拡張が容易にできない。
・スケーラビリティが担保できない。
・障害への耐性が弱い。(呼び出し元の障害が呼び出し元システムに伝播する。サーキットブレーカーなどの仕組みを入れる必要がある)
イベント通知型
以下はAPIベースではなく、状態変化をイベントとして「通知する」方式のアーキテクチャです。APIベースでのアーキテクチャとは異なり、イベントとして記述 することで状態変化を同時に複数のシステムに通知することも可能です。このアーキテクチャの場合、非同期となるため、障害への耐性が強く、システム間をより疎結合にすることができます。
一方で、このアーキテクチャでもどのサービスに処理を依頼すべきかという関心 と対象サービスへの依頼手順が依頼元のシステムに含まれてしまうため、ドメイン間の独立性を十分に担保できない。という課題が残っています。
イベント・ドリブンアーキテクチャ
イベント・ドリブンアーキテクチャではイベント通知型のように
マイクロサービス毎に異なるドメイン・ビジネスロジックが実装され、各マイクロサービスはイベントによりトリガーされ動作します。各マイクロサービスは独立し、イベント以外は互いに認識する必要はありません。
イベント・ドリブンアーキテクチャ
マイクロサービス毎に異なるドメイン・ビジネスロジックが実装され、各マイクロサービスはイベントによりトリガーされ動作します。各マイクロサービスは独立し、イベント以外は互いに認識する必要はありません。
なぜイベント・ドリブンなのか?
「イベント・ドリブンアーキテクチャと従来のアーキテクチャの違い」で記載したとおり、マイクロサービス化に伴い、ドメイン毎にソフトウェアがモジュール化されていても、APIによる同期型の「リクエスト・リプライ」型やでの接続だけでは、十分な疎結合なアーキテクチャにはなりません。
同様に
これらの課題を解決し、ドメイン分割とマイクロサービス化の効果を十分得るためには、より疎結合な仕組み・アーキテクチャが必要となります。イベント・ドリブンアーキテクチャ(Event-Driven Architecture:EDA)では、マイクロサービス毎に異なるドメイン・ビジネスロジックが実装され、各マイクロサービスはイベントによりトリガーされ動作します。各マイクロサービスは独立し、イベント以外は互いに認識する必要はありません。
マイクロサービスアーキテクチャに移行することで、ドメイン毎に分割したシステム・サービスを疎結合かつ、拡張性が高い状態に維持できます。
これにより、ビジネスプロセスにしたがって接続可能な状態を実現が可能になります。(ビジネスプロセスの変更、追加が容易な状態)
- リアルタイム処理
- スケーラビリティ
- ローカップリング(疎結合)
- 変更の容易さ
- レジリエンス(弾力性)
- ビジネスプロセスのリエンジニアリング
リアルタイム処理は、リアルタイムなデータ処理やイベントに応じたリアクションが可能となります。非同期メッセージングを使用してメッセージやイベントの送受信を行って、他のシステムにイベントを通知したりする際に、待ち時間を最小限に抑え、即座に次の処理を進めることができます。また、ストリーム処理は、データストリームを逐次的に処理し、リアルタイムな結果を生成します。
スケーラビリティは、イベントバス(Event Bus)などがあらゆる量のデータを簡単に処理できて、システムが必要に応じてリソースを追加できることを意味します。これにより、高トラフィックの場合でも、システムをスケールアップ可能です。
ローカップリング(疎結合)は、イベントバス(Event Bus)を介してイベントを伝達することで、コンポーネント同士の直接的な依存性を避けることを可能とします。メッセージキューでは、イベントを配置することで、イベントの受け手がイベントの発行元に依存せずにメッセージを取得し処理できます。
イベントソーシング(Event Sourcing)では、データの変更が直接行われるのではなく、変更に関連するイベントが記録されます。
各コンポーネントは、イベントの履歴を利用してシステムの状態を再構築できるため、データへの直接的なアクセスや依存性を避けることができます。
変更の容易さは、EDAの前提となる、各コンポーネントがイベントに基づいて相互作用するため、コンポーネント同士の依存性が緩和されることになります。このイベント駆動のアプローチにより、各コンポーネントは独立して進化できるため、変更が必要な場合でも、他のコンポーネントへの影響を最小限に抑えることができます。
レジリエンス(弾力性)は、ローカップリング(疎結合)とも関連しますが、それぞれのサービスはイベントルーターのみを認識し、お互いを認識しなくなります。1 つのサービスに障害が発生した場合でも、残りのサービスは稼働し続けることができ、イベントルーターは、ワークロードの急増に対応する弾力性のあるバッファとして機能します。
ビジネスプロセスのリエンジニアリングは、これらの要素を組み合わせてイベント駆動アーキテクチャーに移行することにより、サービスや、業務ごと見に直しを行うことで、既存システム全体を、組織の現在のニーズに合わせて新しいシステムに置き換える変革が可能となります。
事業成長のためにシステムを継続的に変化・成長させることが可能となる(事業要件への追従性)
EDA を意識的に組み込んでいかないと、ドメイン分割されたマイクロサービスアーキテクチャのシステムであっても、他ドメイン依存になる「リクエスト・リプライ」型での接続が主体となる可能性があり、これでは疎結合なアーキテクチャとはなりません。
従って、マイクロサービスの効果 (疎結合、高拡張性) を効率的に引き出すためには、イベンドドリブンの必要性が高くなり、これまで記載してきましたアプローチを取り入れることで、リアルタイム処理の実現や、ビジネスプロセスの変更/追加が容易な状態になることが期待できます。
デザインパターン
EDAには、複数のデザインパターンが存在します。これらのパターンは、異なるシナリオに対してEDAを適切に適用するためのガイドラインやベストプラクティスを提供します。以下に、代表的なイベント駆動アーキテクチャのデザインパターンをいくつか紹介します。
- Sagaパターン
- Choreography (コレオグラフィ)
- Orchestration (オーケストレーション)
- コマンドクエリ責任分離(CQRS)
- CQRS とイベントソーシング
- ストラングラーパターン
Sagaパターンとは、結果整合性を使ったアーキテクチャであり、複数の状態変更を調整でき、リソースを長時間ロックすることがないよう設計されたアーキテクチャパターンです。関連するステップを独立して実行できる、個別のアクティビティとして、ビジネスプロセスを明示的にモデル化します。
複数にまたがるサービスごとでトランザクションを分解し、独立して実行できるので、各サービス内では、あらゆる状態変更をローカルACIDトランザクション内で処理できます。更に、1トランザクションあたりにかかる時間を短縮し、複数行、テーブル全体を長時間ロックすることなく処理を実行できます。
個々のトランザクションに分割されているために、障害などが発生すると、既にコミットされているため、単純なロールバックは現実的ではありません。
よって、このパターンでは、補償トランザクションという考え方があり、単純なロールバックができない代わりに、一連のトランザクションの逆方向リカバリによるロールバックを実施します。
但し、この sagaパターンは、サービス間で更新処理が前提となってしまうような、非常に複雑なデザインであるので、「機能ごとに適切にサービス分割する」という一番最初の設計や目的に反している面もあるので、十分な検討が必要では無いでしょうか。
続いて、Sagaパターンの2つの実装方法についてです。
Choreography (コレオグラフィ)では、複数の連携するサービス間で責任を分散することを目的とし、参加する要素がイベントを交換します。他のサービスでローカルトランザクションをトリガーするイベントが、各ローカルトランザクションによって発行されることになります。
Orchestration (オーケストレーション)では、中央のコントローラ(オーケストレータ)が、どのローカルトランザクションを実行するかを、定義し、参加する要素に伝えます。 オーケストレーターは、すべてのトランザクションを処理し、各タスクの状態に応じて、補正トランザクションを使って障害復旧も行うことになります。
コマンドクエリ責任分離(CQRS)の前に、従来のアーキテクチャでは、読み取り側でさまざまなクエリが実行され、形式の異なる複数のデータ転送オブジェクト (DTO) が返される場合もあり、オブジェクトのマッピングが複雑になる可能性があります。 また書き込み側のモデルでは、複雑な検証とビジネス ロジックが実装される可能性があり、モデルが複雑になります。
コマンドクエリ責任分離(CQRS)は、一般的に行われる、データの操作と取得の両方を行う単一のモデルを持つのではなく、読み取り(クエリ)と書き込み(コマンド)の責任を別のモデルによって処理されます。コードで実装された読み取りモデルと書き込みモデルは、個別のユニットとしてデプロイできます。
CQRSとイベントソーシング は、組み合わせて利用が効果的と言われております。例えば更新処理が高速かつ堅牢、クエリにスケーラビリティとパフォーマンスが求められるケースでは以下のような構成が考えられます。
CQRSにおけるCommand(書き込み)部分の実装にイベントソーシングを適用することで、状態の履歴であるイベントを持ちながら、更新処理を可能にします。書込データストアと読取データストアの間にはkafkaなどのイベントサービスを入れることにより、イベントをデータウェアハウスへ投入したり、別のサービスでイベントを購読することでイベント・ドリブンなサービスの追加も可能となります。
Query(読み込み)部分の実装には非正規化されたデータベースを用意し、Queryロジックはこのデータベースに対して行わせる事によって、パフォーマンスとスケーラビリティの確保が行えます。
ストラングラーパターンは、特定の機能を新しいアプリケーションやサービスに徐々に置き換えることで、既存システムを段階的に移行する方法です。
既存システムとユーザーのフロントエンドの間に、データを振り分けるストラングラーファサードを設置し、ステップごとに一部の機能を切り出してマイクロサービス化し、新しいシステムとして稼働させていきます。
単一のモノリスである既存サービスが4つ存在していたとして、そこから、メッセージング機能または新規でメッセージング機能を切り出して、サービスとして稼働させ、その後、順次3つの機能も分割していきます。
但し、既存を分割する部分はかなり難易度が高く、最終的に分割仕切れずに断念してしまう可能性もある。よって、既存の分割よりも新規サービスを既存と分割するスタイルの方が現実的である。
まとめ
次回以降では、「結果整合性とビジネス・顧客体験の設計」についても紹介していきたいと思います。イベント・ドリブンアーキテクチャは新しいマイクロサービスのアプローチでもあり、ドメインの分割、ビジネスロジック・ビジネスロジックを踏まえた整合性の設計が必要で、実際のシステムに導入していくにはドメイン設計、UX設計、ビジネス設計を含めた統合的な設計が必要となります。一方でこのような設計に対するナレッジ・知見はまだまだ少ない状況です。
今回の記事やこのシリーズが皆さんのイベント・ドリブンアーキテクチャ(Event-Driven Architecture)への探求、興味、更に導入のきっかけにつながれば大変幸いです。