【翻訳】「イベント駆動」と言ったら、あなたは何を指しますか?

この記事は、Martin Fowler 氏の What do you mean by “Event-Driven”? を翻訳したものです。 もともと Serverless Architectures を翻訳しようとしていたのですが長すぎて諦めました。。 イベント実施状態転送 (Event-Carried State Transfer) の訳がイマイチしっくりこない。

(Martin Fowler 氏の許可を得て掲載しています)


去年の年の暮れに私は ThoughtWorks の同僚とともにワークショップに参加し、「イベント駆動」の本質について議論しました。ここ数年で、私たちは沢山のイベントを用いたシステムをいくつも開発してきましたが、それらは時に賞賛され、時にこき下ろされました。私たちの北米オフィスはサミットを開催し、世界中から ToughtWorks のシニアデベロッパーたちが集まってアイディアを共有しました。

このサミットの最大の収穫は、人々が「イベント」について話すとき、実は全く異なるものを指していることがある、と認識できたことです。私たちは、どんなパターンを用いるのが便利だろうかということを明らかにするために多くの時間をかけました。この記事は私たちが結論づけたものの簡単な要約です。

イベント通知 (Event Notification)

これはシステムがイベントメッセージを送信して、同一ドメインにある他のシステムに通知を送るときに発生するものです。イベント通知で大事な要素は、送信元のシステムは実はレスポンスについてあまり気を遣わないということです。全くレスポンスを期待しなかったり、対処するレスポンスがあったとしても、直接的でないこともしばしばです。イベントを送信するロジックのフローと、イベントの反応に応答するロジックのフローが完全に別れていることもあるかもしれません。

イベント通知は疎結合を暗に示し、構築が非常にシンプルであるという点において素晴らしいものです。しかし、様々なイベント通知をまたがるようなロジックのフローがある場合は問題になります。この問題は、どのプログラムも明示していないフローを確認するのが困難なことがある、ということです。このフローを確認するほぼ唯一の方法として、実際のシステムのモニタリングがあります。これはデバッグやフローの変更が困難かもしれません。危険なのは、スケールの大きいフローが見えなくなってしまうということを認識せずに、イベント通知でシステム分離を容易に行えてしまえるために、数年後に困ったことになるということです。このパターンは非常に便利ですが、罠にはまらないよう気をつける必要があります。

この罠の簡単な例として、イベントが受動攻撃的なコマンドとして用いられる場合があります。イベント送信元システムが受信者に対して何らかのアクションを実行してもらうことを期待するとき、その意図を表すためにはコマンドメッセージを用いるべきにも関わらず、メッセージをイベントとして位置づけているような場合です。

イベントは大量のデータを保持する必要はなく、多くの場合、ただいくつかの ID 情報と、より多くの情報を求められるように送信者へのリンクさえあれば十分です。受信者は変更されたことを知り、その変更の本質の最小限の情報のみを入手できますが、送信者にリクエストを発行し、次に何をすべきかを決定します。

イベント実施状態転送 (Event-Carried State Transfer)

このパターンは、システムのクライアントを更新する際、 次の作業を行うためにサーバーに通信させずにアップデートを実現したい場合に現れます。顧客管理システムの場合、 変更データの詳細を含むイベントによって顧客が詳細情報 (住所など) を変更したら、その都度イベントを発火させるかもしれません。イベントの受信者は自身の顧客データのコピーを変更内容で更新することができるので、次の動作のために大本の顧客システムに通信する必要がありません。

このパターンの明らかな欠点は、大量のデータが動き回り、また大量のコピーが発生することです。しかしこれは豊富なストレージのある時代では問題にはなりません。顧客システムが利用不可になっても受信側のシステムは機能するため、より強い障害許容力が得られます。顧客情報へアクセスが必要なリモート呼び出しが不要なため、レイテンシーも低下します。全てのクライアントシステムからの要求に答える顧客システムへの負荷を気にする必要もありません。しかし、追加の情報が必要になったとき送信者に問い合わせるだけでも全ての状態を維持、整理する必要があるため、受信側に複雑性をもたせてしまうことになります。

イベントソーシング (Event Sourcing)

イベントソーシングの核となる概念は、システムの状態に変更を与えたら、その都度状態の変更をイベントとして記録し、イベントを再処理することで、将来的にいつでも自信を持ってシステムの状態を再構築できる、というものです。イベントストアは重要な情報源であり、システムの状態は、ただそれのみによって派生します。プログラマーにとって、最も良い例はバージョン管理システムです。全コミットのログがイベントストアにあたり、ソースツリーのワーキングコピーがシステムの状態にあたります。

イベントソーシングは多くの課題があります。ここでは踏み込みませんが、よくある誤解について焦点を当てたいとおもいます。イベント処理は非同期である必要はありません。ローカルの git リポジトリをアップデートするケースを考えてみましょう。Subversion のような集中型バージョン管理システムをアップデートする場合と同様に、完全に同期的な操作になります。実際、これらの全てのコミットによって、あらゆる種類の興味深い振る舞いを行うことができます。 git は素晴らしい例ですが、その核であるコミットは根本的にシンプルなアクションです。

もう一つのよくある誤解は、イベントソーシングのシステムの登場人物は誰でもイベントログを理解、アクセスして、有用なデータを決定すべきである、という考えです。しかしイベントログの知識は制限可能です。私はこの記事をあるエディターで書いていますが、エディターはソースツリーの全コミットについて何も知らず、ただディスクにファイルがあると想定しています。イベントソースシステムでの多くの処理は有用なワーキングコピーに基づいて行わせることができます。イベントログは、その情報を本当に必要とする要素のみが扱うことができるようにすべきです。もし役立つのであれば、異なるスキーマで複数のワーキングコピーを保持することもできます。が、通常は主処理とイベントログからのワーキングコピーの派生処理は明確に区別されているべきです。

イベントログを用いて処理を行う際、ワーキングコピーのスナップショットを構築しておくと便利なことがよくあります。これによって、ワーキングコピーが必要になったときに毎回はじめから全てのイベントを処理する必要がなくなります。実は、ここには二元性があります。イベントログは変更のリストとも、状態のリストとも見ることができます。一方から他方を派生させることも可能です。バージョン管理システムは、最高のパフォーマンスを得るために、スナップショットとイベントログの差分を混ぜることがよくあります。 *1

イベントソーシングは多くの興味深い利点があります。それはバージョン管理システムの価値を考えればすぐにわかります。イベントログは強力な監査能力 (会計取引であれば預金残高のイベントソースになります) をもたらします。 イベントログをその時点まで巻き戻せば、過去の状態を再生成することができます。巻き戻す際に仮のイベントを差し込めば、異なる履歴を探すこともできます。イベントソーシングによって、 Memory Image のように、永続性のないワーキングコピーを保持することが自然になります。

イベントソーシングには欠点もあります。イベントの巻き戻しが、その結果が外部システムとのインタラクションに依存する場合に問題になります。この場合、イベントのスキーマの変更をいかに扱うか、時間をかけて明らかにする必要があります。イベントの処理がアプリケーションに多くの複雑性をもたらすことに多くの人が気づいています (しかし私は、ワーキングコピーを派生させるコンポーネントと主処理を行うコンポーネントの分離が甘さの方が、複雑性をもたらすことに強く影響を与えているのではないだろうか、と思っています)。

CQRS

コマンドクエリ責任分離は、情報の読込と書込のためのデータ構造を分離するという概念です。CQRS は厳密にはイベントのことではありません。設計にイベントが全く現れなくてもCQRSを使用することはできるからです。しかし、一般にCQRSは先述のパターンと組み合わせて用いられるため、非常に重要です。

CQRSが有用であるという理屈は、複合的なドメインにおいて、一つのモデルが読込と書込の両方を行うと非常に複雑になりますが、モデルを分離することでシンプルにすることができるということです。これは特に、アクセスのパターンが異なっている、例えば読込が多いが書込はほとんど無い、といったときによく起こります。しかしCQRSを使用する際は、分離されたモデルをもつことで複雑さが増えてしまう、ということに対してバランスをとって行わなければなりません。私の同僚の多くはCQRSが時として誤って用いられることに気づき、CQRSをかなり慎重に使用しています。

これらのパターンを理解する

ソフトウェアの植物学者になったつもりでサンプルを収集したいのですが、この地形が複雑であることに気がつきました。あるプロジェクトで、優秀で経験豊かなプロジェクトマネージャーが私に告げました。イベントソーシングは災害であると。どんな変更も、読込と書込の両方のモデルを更新するという二重の作業が発生するのだから、と。この言葉によって、私はイベントソーシングとCQRSの潜在的な混同に気づくことができました。この問題の原因がどちらにあるか、どうやったら明らかに出来るでしょうか?このプロジェクトのテックリードは、主要な問題は非同期通信であると主張しました。これは確かに複雑性を助長するものとして知られています。しかし、イベントソーシングとCQRSのいずれにとっても必要なものではありません。さらに、これらのパターンは適切な箇所では良く作用し、不適切な箇所では悪く作用するということに注意しなければなりません。しかし、これらのパターンを混同してしまうと、適切な地形がどういったものなのかがわからなくなってしまいます。

私はこの混乱を解消するような論文を書き、それぞれのパターンをどう実現するか、どういったときに使われるべきなのかに関する強固なガイドラインを用意したいと思っています。しかし残念ながらその時間を割くことができていません。この記事が役立てばよいのですが、本当に必要なことを満たせていないことには注意していただきたいです。

参考文献

2006年にさかのぼり、私は P of EAA book の続刊を作成しようと、いくつかのプロトパターンを書いていました。残念ながら、10年経ってもこの仕事を続ける時間はありませんでした。しかし、このとき書きためたものが、ここで読むことができるようになっています。イベントについては、私は Focusing on Events から始めました。これは私の当時のイベント使用に対する考えを要約したものです。月日が経ったものの、ここで書いたものも多くは未だに正当だと思っています。

これらの記事に最も影響を与えたのが Event Sourcing です。これははじめに、過去の、または異なる状態を形成するために巻き戻しを用いる価値について説いています。

Event Collaboration の記事は、イベント通知とイベント実施状態転送のパターンについて触れていますが、両者が混在しています (両者を分けることを考え出したのは今回のワークショップからでした)。

CQRS についての bliki ポストもあります。

Web にはこれらのトピックに関するたくさんの素材があるので、それらを探すのは楽しいことです。ここでは、それらに目を通しておすすめをピックアップする時間が取れなかったため、コメントは避けておきます。

*1:git はファイルとツリーの状態を .git/objects に保存するためイベントソーシングの例ではない、ということを耳することがあります。しかし、システムが変更やスナップショットをその内部ストレージを使うかどうかは、それがイベントソーシングであるかどうかには関与しません。 Git は幸せなことに、必要なときに変更のリストを提示してくれます。またデータをパックファイルに圧縮する際、パフォーマンス上の理由で、スナップショットと変更を組み合わせています。