マイクロサービスのための分散データ 〜 イベントソーシング vs チェンジデータキャプチャ

インテグレーションのためのミドルウェア製品のテクニカルサポートを担当している山下です。

今回は レッドハットのシニアアーキテクトである Eric Murphy さんによる「マイクロサービスのための分散データ 〜 イベントソーシング vs チェンジデータキャプチャ(CDC)」の翻訳記事です。この記事では、イベントソーシング、CDC、CDC + Outboxパターン、CQRSをそれぞれ簡単に説明しながら、それらの特性の違いを比較します。また、イベントソーシングとCQRSの簡易な説明がなされている他、あまり明確に語られることが少ないもののソフトウェアの設計に大きな影響をおよぼすドメインイベントとチェンジイベントの違いにも触れられています。

[原文] Distributed Data for Microservices — Event Sourcing vs. Change Data Capture by Eric Murphy

あんまり翻訳がうまくないのは許してください (^^;


この記事では、イベントソーシング、コマンドクエリの責任分離(CQRS)、チェンジデータキャプチャ(CDC)、およびOutboxパターンの領域をみていきます。 これらのソリューションの価値が明らかになるでしょう。 そして異なるこれらの設計について、それぞれの長所/短所を用いて説明します。

そもそも、なぜこれらのソリューションが重要なのでしょうか? それは多くのチームがマイクロサービスを構築し、データを複数のデータストアに分散しているためです。 マイクロサービスのシステムでは、リレーショナルデータベース、オブジェクトストア、メモリ内キャッシュ、さらには検索インデックスが含まれている場合があります。 データはすぐさま失われたり、同期がとれなくなったり、破損するため、ミッションクリティカルなシステムには悲惨な結果をもたらすことがあります。

これらの深刻な問題を回避するソリューションは、多くの組織にとってとても重要です。しかし 残念ながら、こうした不可欠のソリューションを理解するのは困難でもあります。これはイベントソーシング、CQRS、CDC、およびOutboxでも例外ではありません。これらのソリューションを特定のユースケースにどのように適用できるかを学びそして理解する機会として捉えてください。

この記事の最後でわかるように、これら4つのソリューションのうち、3つは高い価値がありますが、残りの1つは稀な状況を除いては推奨されません。この記事でのアドバイスは、あなたの特有のユースケースに対して評価されるべきです。これら4つのソリューションのどれもが適切ではないこともあるでしょう。

リアクティブシステム

軽く振り返るなら、イベントソーシングとチェンジデータキャプチャ(CDC)は、リアクティブな分散システム(つまりマイクロサービス)の構築に使用できるソリューションです。 マイクロサービスは、レジリエント(回復力)とエラスティック(弾力性)を備えていることにより、絶えず変化する環境(つまりクラウド)に対応する必要があります。 これらの能力の背後にあるマジックは、メッセージとイベント駆動です。 詳細は Reactive Manifesto を読むことをお勧めします。

Figure 1. Attributes of a Reactive System, per the Reactive Manifesto

イベントソーシングとチェンジデータキャプチャ(CDC)の共有のゴール

この記事で紹介する2つのコアソリューションは、イベントソーシングとチェンジデータキャプチャです。 これらのソリューションを紹介する前に、これらが次の共通の目標を達成していることがわかるでしょう:

  1. 1つのデータストアを、特定のデータセットのためのグローバルな真実の情報源(source of truth)として指定する
  2. 過去や現在のアプリケーションの状態を、ジャーナルまたはトランザクションログとも呼ばれる一連のイベントとして表現する
  3. 必要に応じて状態を再構築または最新化するための、イベントを再生することができるジャーナルを提供する

イベントソーシングは独自のジャーナルを情報源(source of truth)として使用し、一方でチェンジデータキャプチャは基礎となるデータベーストランザクションログを情報源(source of truth)として使用します。この違いは、この記事の後半で説明するソフトウェアの設計と実装に大きな影響を及ぼします。

ドメインイベント vs チェンジイベント

深く掘り下げる前に、イベントソーシングとチェンジデータキャプチャに関して、それぞれの関心事となるイベントのタイプを区別することは重要です。

  • ドメインイベント — アプリケーションによって生成される、ビジネスドメインの一部である明示的なイベント。 これらのイベントは通常、OrderPlacedã‚„ItemShippedなど、過去形で表される。ドメインイベントはイベントソーシングの主要な関心事。
  • チェンジイベント — データベーストランザクションログから生成されるイベント。発生した状態遷移を示す。 チェンジイベントはチェンジデータキャプチャにとっての関心事。

ドメインイベントとチェンジイベントは、チェンジイベントにドメインイベントが含まれている場合を除くなら関係ありません。チェンジイベントにドメインイベントが含まれているというのは、この記事の後半で紹介するOutboxパターンの領域です。

それでは、イベントソーシングとチェンジデータキャプチャに関するいくつかの共通点を確立したので、深く掘り下げましょう。

イベントソーシング

イベントソーシングは、ソフトウェアがドメインイベントのジャーナルとしての状態を維持できるようにするソリューションです。 そうしてジャーナル全体でアプリケーションの現在の状態を表現します。 また、このジャーナルを使用すると、履歴を簡単に監査したり、以前の状態を再現してタイムトラベルしたりエラーを再現したりすることができます。

一般的にイベントソーシングの実装には次のような特徴があります:

  1. アプリケーションのビジネスロジックから生成されたドメインイベントが、アプリケーションに対する新しい状態を追加する
  2. アプリケーションの状態は、追記のみのイミュータブルなイベントログ(ジャーナル)を介して更新される
  3. ジャーナルは、アプリケーションの存続期間中、真実の情報源(source of truth)と見なされる
  4. ジャーナルは、どのような時点のアプリケーションの状態でも再構築できるようリプレイ可能である
  5. ジャーナルは、オブジェクト(DDD用語でいうところの集約)の現在の状態を補足するために、ドメインイベントをIDでグループ化する

Figure 2. Representation of Event Sourcing Materializing an Object

また加えて、イベントソーシングの実装には次の特徴があることもあります:

  1. アプリケーションの状態の再作成を高速化するためのジャーナルのスナップショットメカニズム
  2. 必要に応じてジャーナルからイベントを削除するメカニズム(通常はコンプライアンス上の理由から)
  3. アプリケーションの状態を配布するために使用できるイベントディスパッチのためのAPI
  4. 一般的なシステムでは存在するような強い一貫性のあるトランザクション保証は提供されない
  5. ジャーナル内のイベント形式の変更に対処するための下位互換性メカニズム
  6. アプリケーションの真実の情報源(source of truth)であるジャーナルをバックアップおよび復元するメカニズム

イベントソースはデータベースの動作を模倣しますが、しかしそれはアプリケーションレベルにて行われます。 図2をみれば、データベースとほとんど同様の仕組み(図3)で更新されていることがわかるでしょう。

Figure 3. Representation of Database Transaction Materializing a Table

この図2と図3の比較は、イベントソーシングとチェンジデータキャプチャを比較するにつれてより重要になります。

チェンジデータキャプチャ

チェンジデータキャプチャ(CDC)は、データベーストランザクションログ(または同等のメカニズム)からチェンジイベントをキャプチャし、それらのイベントを下流コンシューマに転送するソリューションです。 CDCは、最終的にアプリケーションの状態を外部化し、データの外部ストアと同期することを可能にします。

通常、チェンジデータキャプチャの実装には次の特徴があります:

  1. トランザクションからチェンジイベントをマテリアライズ化する目的のために、データベースのトランザクションログを読み取る外部プロセスである
  2. チェンジイベントは、メッセージとして下流コンシューマーに転送される

ご覧のとおり、CDCは非常に狭い範囲での比較的単純な概念です。それは単にデータベースのトランザクションログを、関心のあるコンシューマに対してイベントストリームとして、外部化するだけです。

Figure 4. Change Data Capture Implementation Options

CDCは、イベントの消費方法に柔軟性与えます。 図4のように:

  • オプション 1 は、トランザクションログからメッセージブローカーにイベントをキャプチャして転送するスタンドアロンCDCプロセス(ソースコネクタ)
  • オプション 2 は、イベントをアプリケーションに直接送信する組み込みCDCクライアント
  • オプション A は、CDCイベントをデータストアに直接永続化する別のコネクタ(シンクコネクタ)
  • オプション B は、メッセージブローカーを介してアプリケーションにイベントを転送

最後に、CDC実装には度々次のような特徴があることもあります:

  1. 少なくとも1回の配信(at-least-once)保証で、イベントをすべてのコンシューマに転送するために、デュラブルなメッセージブローカーが利用される
  2. イベントが保持されている限り、データストアのトランザクションログまたはメッセージブローカーからイベントをリプレイする能力がある

CDCは非常に柔軟で、複数のユースケースに適応できます。 アーリーアダプターのCDCの採用者はオプション 1 や A を選択していましたが、CDCが勢いを増すにつれて、オプション1 や B、そしてオプション 2 の人気が高まっています。

Outboxパターンを実装するためにCDCを利用する

Outboxパターンの主な目的は、(テーブルに格納されている)アプリケーションの状態の更新とドメインイベントの発行を、単一のトランザクション内で行うことを保証することです。これには、データベース内にOutboxテーブルを作成して、トランザクションの一部としてそれらのドメインイベントを収集することを含みます。ドメインイベントとそのOutboxでの転送にまつわるトランザクションの保証は、システム全体のデータ一貫性にとって重要です。

トランザクションが完了すると、CDCコネクタによってドメインイベントが取得され、信頼できるメッセージブローカーを利用して関心のあるコンシューマーに転送されます(図5を参照)。 これらのコンシューマーは、このドメインイベントを使用して、彼ら自身の集約を具体化するかもしれません(上記のイベントソーシングを参照)。

Figure 5. Outbox Pattern implemented with CDC (2 Options)

Outboxは、送信するイベントデータの一時的なストアにすぎず、読み取りやクエリは意図されていないため、アプリケーションからは抽象化されることもあります。実際、Outboxにあるドメインイベントは、挿入後にすぐに削除される可能性があります!

イベントソーシングジャーナル vs Outbox

これで、イベントソーシングジャーナルと、Outboxを用いたCDC の設計のオーバーラップを詳しく見ることができます。ジャーナルの属性をOutboxテーブルと比較することにより、類似性が明らかになります。 DDDでいうところの集約は、イベントソーシングとOutboxの双方にとって、データがどのように保存され消費されるかの中心にあります。

イベントソーシングジャーナルとOutbox間に存在する一般的な属性は次のとおりです:

  • イベントID — イベントそれ自体の一意の識別子。べき等のコンシューマの重複排除にも使用できる
  • 集約ID — イベントに関連したパーティションで利用される一意の識別子。 これらのイベントは集約の状態を構成
  • 集約タイプ — 関心のあるコンシューマーのみにイベントをルーティングするために使用できる集約のタイプ
  • シーケンス/タイムスタンプ — イベントをソートして順序を保証する方法
  • メッセージペイロード — 下流コンシューマーが読み取り可能な形式で交換されるイベントデータを含む

Outboxテーブルとイベントソーシングジャーナルは基本的に同じデータ形式を持ちます。主な違いは、イベントソーシングジャーナルは永続的で不変のドメインイベントのストアであるのに対し、Outboxは非常に短命であり、ドメインイベントがチェンジイベント内に記録され、下流コンシューマに転送されるただの降着場であることです。

コマンドクエリの責任分離(CQRS)

コマンドクエリの責任分離パターン(略してCQRS)は、一般的にイベントソーシングと共に利用されます。ただし、CQRSを使用するためにイベントソーシングは必要ありません。たとえば、代わりにOutboxパターンで実装できます。

CQRSとは何でしょうか?これは、いくらかのまとまったデータをクエリ可能な読み取り専用のビューにすることを主な目的として、プロジェクション(投影)と呼ばれるデータの代替表現を作成するパターンです。さまざまなクライアントが関心のある同じデータセットに対して、複数のプロジェクションがある場合があります。

CQRSのコマンド面では、アクション(コマンド)を処理し、そして最終的にプロジェクションされる状態を作成するために利用できるドメインイベントを生成する、アプリケーションに当てはまります。これが、CQRSがイベントソーシングに頻繁に関連付けられる1つの理由です。

CQRSがイベントソーシングと組合わされるもう1つの理由は、ジャーナルはアプリケーションによってクエリできないためです。 イベントソースシステムでデータをクエリする唯一の方法は、プロジェクションを使用することです。 これらのプロジェクションは結果整合性であることに留意してください。これにより柔軟性がもたらされますが、強い一貫性のあるビューからの逸脱と複雑さをも引き起こします。

Figure 6. Representation of Event Sourcing with CQRS

Figure 7. Representation of Event Sourcing with CQRS using a Message Broker

図6と図7から分かるように、これらはイベントソーシングに基づくCQRSパターンの2つの異なる解釈ですが、最終結果は同じで、イベントからのみ生じるデータのクエリ可能なプロジェクションになります。 前述しましたが、CQRSは図8のようにOutboxパターンと組み合わせることもできます。この設計の利点はアプリケーションデータベース内で依然として強力な一貫性があることですが、しかしCQRSプロジェクションを用いた結果整合性となります。

Figure 8. Representation of the Outbox Pattern with CQRS

ドメインイベントをアプリケーション内部で処理する

この記事の中では、システム全体にデータを配布することに重点を置いていますが、アプリケーション内部でドメインイベントを使用することも重要です。 ドメインイベントを内部で処理することは、イベントの発生元と同じマイクロサービスコンテキスト内でのビジネスロジックの実行などの、さまざまな理由で必要となります。 これはイベント駆動アプリケーションを構築する一般的な方法です。

イベントソーシングまたはCDCのいずれかを用いて、ドメインイベントを内部的に処理するには、イベントをメモリで渡すディスパッチャメカニズムが必要で、これには Vert.x EventBus、Akka Actor System、あるいはSpring Application Events といった例があります。 しかしながらOutboxパターンの場合には、イベントはOutboxトランザクションが正常に完了した後にのみディスパッチされます。

特性の比較

この記事はたくさんの事を説明したので、要約した表が有益かもしれませんね:

特性 イベントソーシング CDC CDC + Outbox CQRS
目的 ドメインイベントを含むジャーナルの状態をキャプチャ トランザクションログからチェンジイベントをエクスポート CDCを介してOutboxからドメインイベントをエクスポート ドメインイベントを使用して、データのプロジェクションを生成
イベントタイプ ドメインイベント チェンジイベント チェンジイベントに組み込まれたドメインイベント ドメインイベント
真の情報源(Source of Truth) ジャーナル トランザクションログ トランザクションログ (実装に依存する)
境界(Boundary) アプリケーション システム システム (CDC) アプリケーション (Outbox) アプリケーションもしくはシステム
一貫性モデル N/A (ジャーナルにのみ書き込み) 強い一貫性 (テーブル)、結果整合性 (Change Event capture) 強い一貫性 (Outbox)、結果整合性 (Change Event capture) 結果整合性
リプレイ可能 Yes Yes Yes (実装に依存する)

イベントソーシング + CQRS の 長所と短所

イベントソーシングとCQRSを理解したので、イベントソーシングとCQRSを組み合わせた場合の長所と短所を確認しましょう。これらの長所と短所は、利用可能な現在の実装を考慮し、分散システムを構築する私自身や他のエキスパート両方からの経験をもとに文書化しました。

イベントソーシング + CQRS の 長所

  1. 監査を目的としてジャーナルに簡単にアクセスできる
  2. 一般的にジャーナルへの大量の書き込み操作に対するパフォーマンスが高い
  3. 大量データに対してジャーナルをシャードできる可能性がある(ただしデータストア依存)

イベントソーシング + CQRS の 短所

  1. すべては結果整合性のデータです。 強い一貫性データの要件はイベントソーシングとCQRSには適合しません
  2. ジャーナルへの書き込みは読み取ることができません(クエリ観点から)
  3. ジャーナルおよびイベントソースアーキテクチャ周りの長期メンテナンスの考慮
  4. エラーケースための補正アクションのための多くのコードを書く必要がある
  5. デュアル書き込みの欠陥を解決するためのトランザクション保証がない(後で説明します)
  6. イベントの形式が変化するために、レガシーデータの下位互換性または移行を検討する必要がある
  7. ジャーナルのスナップショットとそれに関連する影響を考慮する必要がある
  8. イベントソーシングとCQRSを使用した経験のある開発者は事実上存在しません
  9. イベントソーシングのユースケースが不足しているため適用が制限される

イベントソーシングとCQRSのデュアル書き込みのリスク

イベントソーシングの1つの問題は、アプリケーションにエラーがある場合、CQRSプロジェクションを更新することに失敗する可能性があることです。 これによりデータが失われる可能性があります。残念ながら、アプリケーション自体に適切な補正アクションを組み込まなければ、そのデータを回復することは困難かもしれません。 追加コードと複雑さは開発者に委ねられエラーが発生しやすくなります。 たとえば1つの回避策は、イベントソースジャーナルに相関する読み取りオフセット番号を追跡し、エラー時にドメインイベントを再処理してCQRSプロジェクションを更新できる、リプレイの能力を与えることです。

このエラーが発生する根本的な理由は、ジャーナルとCQRSプロジェクションの両方に書き込むためのトランザクションを欠いていることです。これは「デュアル書き込み」と呼ばれるものであり、エラーのリスクが大幅に増加します。 このデュアル書き込みの欠陥を図9に示します。

Figure 9. Lack of Transactional Integrity with Event Sourcing and CQRS

図7に示すように、メッセージブローカーを追加しても、デュアル書き込みの問題は解決しません。この設計では、メッセージブローカーにメッセージを書き出しているため、エラーが発生する可能性があります。 デュアル書き込みの欠陥は、CQRSでイベントソーシングを使用する際の課題のほんの一例です。 さらに、ジャーナルを真実の源泉(source of truth)として持つことによる長期的なメンテナンスとDay 2の影響により、時間の経過とともにアプリケーションのリスクが高まります。 また、イベントソーシングは、ほとんどのエンジニアには馴染みのないパラダイムであり、システムを再設計することにつながる誤った憶測や設計上の選択をしやすくなります。 イベントソーシングの長所と短所をCQRSと組み合わせて考えると、この設計に着手する前に代替案を探すことをお勧めします。 あなたのユースケースはイベントソーシングに適合するかもしれませんんが、CDCが適合する場合もあります。

CDCとOutbox のためのDebezium

Debeziumは、Red HatがサポートするオープンソースCDCプロジェクトであり、過去数年間で徐々に人気を集めています。最近、Debeziumは、Quarkus Javaマイクロサービスランタイムの拡張機能を備えたOutboxパターンの完全なサポートを追加しました。Debezium、Quarkus、およびOutboxは、デュアル書き込みの欠陥を回避する包括的なソリューションを提供し、一般的にイベントソースソリューションと比較して、平均的な開発者チームにとってより実用的なソリューションです。

Figure 10. Error Handling of the Outbox Pattern with CQRS

CDC + Outbox with Debezium の長所

  1. 真実の源泉(Source of truth)は、アプリケーションデータベースのテーブルとトランザクションログ内にとどまります
  2. トランザクションの保証と信頼できるメッセージングにより、データの損失または破損の可能性が大幅に減少します
  3. プロトタイピカルなマイクロサービスアーキテクチャに適合する柔軟なソリューション
  4. シンプルな設計は、長期にわたって維持しやすい
  5. 自身の書き込みを読み取りとクエリが可能
  6. アプリケーションデータベース内の強力な一貫性の機会がある。 残りのシステム全体にまたがる結果整合性

CDC + Outbox with Debezium の短所

  1. トランザクションログを読み取り、メッセージブローカーを通過することにより、追加のレイテンシが発生する場合があります。 レイテンシーを最小化するためにチューニングが必要な場合があります。
  2. Quarkusは優れていますが、既製された Outbox API の現在の唯一のオプションです。しかし 必要に応じて独自の実装をロールすることもできます。

まとめ

マイクロサービスを使用した分散システムの構築は、非常に困難な場合があります。 それが、イベントソーシングのような斬新なソリューションを検討する上で魅力的な理由です。 ただし、通常、Debeziumを使用するCDCおよびOutboxは、イベントソーシングのより優れた代替手段であり、さらにはCQRSパターンと互換性があります。 一部のユースケースではイベントソーシングに価値がある場合がありますが、まずDebeziumとOutboxを試してみることをお勧めします。

さらに先へ進むためのリソース

ドキュメント

  1. Debezium Tutorial
  2. Debezium Outbox Event Router
  3. Debezium Outbox Pattern Sample Application (Quarkus)
  4. Quarkus Getting Started

ブログと記事

  1. Reliable Microservices Data Exchange With the Outbox Pattern
  2. Transactional Outbox Pattern
  3. Outbox Event Router goes Supersonic!
  4. Introducing Event Sourcing
  5. Exploring CQRS and Event Sourcing
  6. What they don’t tell you about event sourcing
  7. Day Two Problems When Using CQRS and Event Sourcing
  8. Introducing Derivative Event Sourcing
  9. Domain Events versus Change Data Capture
  10. Reactive Manifesto

ビデオとポッドキャスト

  1. Gunnar Morling on Change Data Capture and Debezium
  2. Microservices & Data: Implementing the Outbox Pattern with Debezium

    Eric Murphy

Ericha はレッドハットのシニアアーキテクトです。彼はアプリケーション開発とOpenShiftのコンサルティングプロジェクトをリードしています。 Eric はシアトルのそばに住んでいます。 [twitter] [github]

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。