イベントに着目する

2006年1月25日



これは、2000年代半ばに執筆していた「Further Enterprise Application Architecture development(エンタープライズアプリケーションアーキテクチャ開発のさらなる発展)」の一部です。残念ながら、それ以来、他の多くのことに時間をとられてしまい、これらをさらに進める時間がありませんでしたし、近い将来にも時間が見込めません。そのため、この資料は非常に草稿段階のものであり、再び作業に取り組む時間ができるまで、修正や更新は行いません。

エンタープライズアプリケーションについて考える最も古くからの方法の1つは、外部からのイベントに反応するシステムと見なすことです。これは、80年代後半に構造化設計コミュニティで確立された考え方です。今では「イベント駆動型アーキテクチャ」という名目で耳にするでしょう。

この考え方は、システムと外界との相互作用をイベントの伝達として捉えることに焦点を当てています。入力は、システムへの各入力と外部世界のイベントを結びつけたイベントリストを作成することで理解されます。同様に、システムは、外部システムにイベントを通知することで、自身に生じた重要な変更をすべて発表します。

システムイベントをこのように見ることは、システムの設計プロセスの一部となる可能性があります。この良い例は、後の構造化分析で使用されたイベント分割の技法です。また、イベントオブジェクトを明示的に作成し、処理のために何らかのハンドラに供給することで、実装においてシステムに組み込むこともできます。

このようにイベントに注目することは、複数のシステムを統合することに大きな関心を集めています。これがイベント駆動型アーキテクチャの推進力となっています。イベントメッセージを使用することで、送信者と受信者を、ID(イベントに応答する人を気にせずにイベントをブロードキャストする)と時間(イベントをキューに入れて、受信者が処理の準備ができたときに転送できる)の両方において簡単に分離できます。このようなアーキテクチャは、この疎結合により、スケーラビリティと変更可能性に大きく貢献します。

しかし、このセクションでは、これらの組み合わせにはそれほど関心がありません。それらは価値がありますが、むしろ、イベントフォーカスが単一のアプリケーションにもたらすものに焦点を当てています。これらを見てきた中で、いくつかの非常に興味深い特性に気づき、それが非常に興味深い機会を生み出しました。これらの多くはかなり断片的であったため、頭の中で整理するのが非常に困難でした。しかし、ここには、通常アプリケーションでは提供されない非常に興味深い機能のための機会があります。

イベントの表現

これまでイベントについて漠然と語ってきましたが、もう少し深く掘り下げるにつれて、イベントをより詳しく見ていく必要があります。本質的に、ドメインイベントは、アプリケーションにとって関心のある外部で発生したことを示します。これは、イベントを記述するデータ(ソースデータと呼ぶ)を伴う何らかのデータ構造でアプリケーションに送信されます。イベントは、メッセージングシステム、ユーザーインターフェース、データベーステーブルのトリガーなど、さまざまな場所から発生する可能性があります。

イベントは、多くの異なるものを表すことができるため、ソースデータは大きく異なります。ただし、イベントが持つべき2つの重要なことは、タイムポイントです。常に考慮すべき2つの異なるタイムポイントがあります。イベントが発生した時刻と、システムがイベントに気づいた時刻です。(複雑なシステムでは、複数の気づき時間がある場合もあります。)これらのタイムポイントは、時間的パターンで説明する実際と記録の概念と密接に対応しています。

イベントは、ソースデータだけでなく、イベントに対して行った処理を記述する処理データも持ち運ぶ場合があります。この2つを区別することが重要です。特に、イベントのソースデータは不変です。これは、発生した内容について知っていることであり、簡単には変更できません。(ここに落とし穴があります。間違ったソースデータを取得する可能性があります。これについては後ほど説明します。)

ドメインイベントはオブジェクトであるため、記録が容易です。少なくとも優れた監査証跡となるため、通常はドメインイベントをログに記録して記録を保持することは理にかなっています。

イベントを用いたコラボレーション

私たちは、プログラムを連携して動作する複数のコンポーネントに分割することに慣れています。(ここでは、あいまいな「コンポーネント」という言葉を使用しています。これは、プログラム内のオブジェクトやネットワークを介して通信する複数のプロセスなど、多くのことを意味するためです。)コンポーネントを連携させる最も一般的な方法は、リクエスト/レスポンススタイルです。顧客オブジェクトがセールスマンオブジェクトからデータを取得したい場合、顧客オブジェクトはセールスマンオブジェクトのメソッドを呼び出して、そのデータを要求します。

もう1つの連携スタイルは、イベントコラボレーションです。このスタイルでは、あるコンポーネントが別のコンポーネントに何かをするように要求することはありません。代わりに、各コンポーネントは何かが変更されたときにイベントを通知します。他のコンポーネントはそのイベントをリッスンし、思い通りに反応します。よく知られているオブザーバーパターンは、イベントコラボレーションの例です。

イベントコラボレーションは、オブジェクトが責任を果たすことに関する多くの前提条件を変更します。特に、状態の保存に関する責任が変わります。リクエスト/レスポンスコラボレーションでは、特定のデータを1つのコンポーネントのみが保存するように努め、他のコンポーネントは必要なときにそのコンポーネントにデータを要求します。イベントコラボレーションでは、すべてのコンポーネントが必要なすべてのデータを保存し、そのデータの更新イベントをリッスンします。リクエスト/レスポンスコラボレーションでは、データを保存するコンポーネントが通常はデータを更新する責任を負いますが、イベントコラボレーションでは、データを更新する責任を負うコンポーネントはデータをまったく保存する必要がなく、更新時にイベントが発生することを保証するだけで済みます。

アプリケーションでイベントを使用する場合、イベントコラボレーションは必須ではありません。また、イベントコラボレーションとリクエスト/レスポンスの選択は排他的な選択ではありません。一般的には、2つのスタイルが混在しており、通常はリクエスト/レスポンスが支配的です。

イベントコラボレーションは、既存のコンポーネントを変更することなく、システムに新しいコンポーネントを簡単に追加できる非常に疎結合を実現します。イベントコラボレーションの欠点は、コラボレーションを理解するのが非常に難しいことです。リクエスト/レスポンスコラボレーションは、フロー全体を示す何らかの形式のコードで指定されますが、イベントコラボレーションははるかに暗黙的です。そのため、予期しないことが発生した場合のデバッグがはるかに困難になります。

イベントソーシング

しかし、監査証跡以上の非常に興味深い領域に進むことができます。これを可能にするのは、システムへの*すべて*の変更がイベントによって引き起こされる場合です。このアプローチをイベントソーシングと呼びます。別の見方をすれば、ドメインイベントのログを処理することでアプリケーションの状態を完全に導き出せる場合に、イベントソーシングが発生します。

この状況は、機能面と実装面の両方で、いくつかの興味深い機会を開きます。すぐに実装できる機会は、アプリケーションの状態全体をメモリ内に保持し、永続データベースを完全に不要にすることです。アプリケーションがクラッシュした場合は、イベントを再実行します。おそらく、アプリケーション状態の定期的なスナップショットから実行します。システムは、パフォーマンス上の理由またはシンプルさの理由から、この方法を選択する場合があります。システムから永続化ロジックを削除すると、システムの構築と保守に必要な労力が大幅に削減されます。もちろん、すべてのシステムがこのルートを選択できるわけではありませんが、一部のシステムにとっては有用なオプションです。

アプリケーションの状態は処理されたイベントによって完全に定義されるため、ドメインイベントの代替リストを処理することで、代替アプリケーション状態(並列モデルと呼ぶ)を構築できます。特に、これらにより、過去の特定の時点でのアプリケーション状態を構築して、なぜ誰かが何をしたのかを調べたり、過去と現在の変更を比較したりするために、過去に遡ることができます。過去を調査するだけでなく、代替イベントストリームを探索することで、まだ発生していないことを検討することもできます。木曜日にその嵐がオヘア空港を1時間ではなく2時間ノックアウトしていたらどうなっていただろうか?配送サイクルを1日1回から1日2回に変更すると、運用はどのように変わるだろうか?

並列モデルはエンタープライズアプリケーションでは一般的ではありませんが、エンタープライズアプリケーションの開発者には非常になじみがあります。開発者のツールキットに不可欠なソースコード管理システムは、イベントソーシングを使用してオンデマンドで並列モデルを作成するシステムです。これらの並列モデルは履歴的なものである場合もあれば、分岐を通じて複数の現実を可能にする場合もあります。その結果、ソースコード管理システムは、並列モデルの利点と問題を調査するための貴重なメタファーを提供できます。

イベントストリームを操作することでできるもう1つの興味深いことは、誤った情報の結果に対処することです。前述したように、イベントソースデータは不変です。受信して処理したものを変更することはできません。しかし、受信したものが間違っていた場合はどうでしょうか?イベントソーシングを使用している場合、イベントが発生した時点に戻り、発生するはずだったことをキャプチャする新しい並列モデルを構築できます。本質的に、その間違ったイベントを新しい遡及的イベントに置き換えます。実際、通常は複雑で費用のかかる手作業が必要となるこのプロセスを完全に自動化することは驚くほど簡単な場合があります。

そのため、イベントソーシングは素晴らしいように聞こえます。彼らはセーフウェイでそれを手に入れているのでしょうか?残念ながら、落とし穴がいくつかあります。最も簡単な落とし穴はプログラミングモデルです。永続化可能なイベントの形式でシステムへのすべての更新を取得するのは面倒です。特に、対話性の高いシステムでは面倒です。また、不自然なので、慣れるまで少し時間がかかります。

しかし、イベントソーシングの真の難しさは、イベントを中心としない外部システムとの連携にあります。イベントをリプレイするには、外部システムの過去の状態をクエリできる必要があります。ダウンストリームシステムへの重複した更新の送信を回避する必要があります。並列モデルの模範であるソースコード管理システムは、この種の動的統合を行う必要がないため、これを回避できます。しかし、多くのエンタープライズアプリケーションは、自身の作業と同じくらい統合に関わっているため、これらの問題は大きくのしかかります。

これらの問題は克服できないものではありません。代替処理を行っている量、外部システムとの統合の度合い、そしてそれらの外部システム自体がイベントとイベントコラボレーションを使用している度合いによっては、解決する価値があるかもしれません。 イベントソーシングのより高度な結果が得られなくても、そのようにシステムを設計することで、優れた監査機能と、後でより洗練された方向に進むことができる基盤が提供されます。イベントソーシングを後付けするのは非常に面倒なようです(そう言うのは、そのように後付けしたシステムに出会ったことがないからです)。

考慮すべきもう1つの点は、システム全体でイベントソーシングを使用する必要がないことです。実際、ほとんどの場合、イベントソーシングはシステムの一部、主に会計側で使用されているのを見てきました。実際、これらのパターンを書き留めようとした以前の試みは、会計の観点から行われました。それが私がそれを見た場所だからです。会計は、イベントソーシングが提供する種類の強力な監査を必要とするだけでなく、イベントソーシングの問題のいくつかを軽減するのにも役立ちます。

イベントの処理

イベントがシステムの更新のすべてを伝達するか、一部のみを伝達するかに関わらず、イベントはそれらを処理する処理ロジックを編成するための興味深い代替手段を提示します。

特に興味深いスタイルの1つは、イベントのソースデータを調べて、どの実際のパフォーマーオブジェクトがイベントを処理するかを決定するディスパッチャーオブジェクトの使用です。これにより、パフォーマーオブジェクトはシンプルになり、ディスパッチャーには正しいパフォーマーを見つけるために必要なビジネスロジック以外は含まれません。

概念的には、このようなディスパッチャーを編成できる方法はたくさんありますが、私が見てきた繰り返し発生するスタイルの1つは、アグリーメントディスパッチャーです。ここで、ディスパッチメカニズムの中心となる組織的特徴は、イベントのコンテキストを管理する契約上の合意です。顧客に対して行われた販売は、継続的な契約、または一般的な販売ポリシーを時折の顧客に規定する暗黙の合意によって管理される場合があります。このスタイルは、ディスパッチャーがイベントを調べてどの契約に送信するかを調べ、契約オブジェクトがさらに一連の条件チェックを実行し、最終のパフォーマーに到達するまでチェーンが続行される、一連の委任を促します。

強みは、パフォーマーとイベントのマッチングのロジックの多くを、オブジェクト間の関係、つまり事実上データとして設定できることです。これにより、システムは高度な構成可能性を備えることができます。特に、このオブジェクト関係のWebに時間を含めることで、イベント処理におけるもう1つの一般的な問題、つまりビジネスルールの更新への対処に対処できます。

イベントとコマンド

この議論では、アプリケーションの状態に対するすべての変更をイベントを通じてカプセル化することを指しますが、単語(およびパターン)「コマンド」を使用して、このすべてを簡単に述べることができます。イベントは明らかにコマンドのほとんどのプロパティを共有しています。独自のライフタイムを持ち、キューに入れられて実行される機能、イベントの反転はコマンドの取り消しと同じです。

イベントを使用する理由の1つは、この種の動作に対して、この用語が現場で広く使用されていることです。イベント駆動型アーキテクチャやイベントメッセージなどの用語がよく聞かれます。

最終的に私はイベントを選びました。なぜなら、イベントには微妙ですが重要な一連の関連付けがあると思うからです。人々はコマンドを要求をカプセル化するものであると考えています。コマンドを使用すると、システムにXを実行するように指示します。しかし、イベントは何かが起こったことを伝えるだけです。イベントを使用すると、システムにYが起こったことを知らせることができます。もう1つの違いは、興味のあるすべての人にイベントをブロードキャストすることを考える一方で、コマンドは特定の受信者にのみ送信することです。実際の動作に関しては、実際には問題ありません。Xコマンドに対するシステムの多態的な反応は、Yイベントに対する反応と変わりません。それでも、名前付けは人々がイベントやどのような種類のイベントを作成するかについてどのように考えるかに影響を与えると私は思います。

コマンドは、[GoF(Gang of Four)]で説明されている古典的なパターンです。[ HohpeとWoolfのエンタープライズ統合パターン]も参照して、コマンドメッセージイベントメッセージの違いを確認してください。


主な改訂

2006年1月25日:イベントコラボレーションの概要を追加

2005年12月12日:最初のドラフトをオンラインで公開