(CJP) 多くの開発者にとって、イベント ソーシングは非常に複雑で、しばしば分散するプロジェクトを構築するために使用される魔法の獣です。 これには十分な理由があります。イベント ソーシングは、これらの複雑なプロジェクトに非常によく適合する方法でコードを構築することを強制するパターンです。
「モジュール化」、「分散」、「スケーラブル」、「多用途」などの言葉がそれを説明するために頭に浮かびます。 大規模なアプリケーションを構築している場合は、なくてはならない特性です。 人気のある Web ショップや銀行を考えてみてください。1 秒間に数百万とまではいかなくても、数千のトランザクションを処理しています。 これらは、イベントソーシングが最も頻繁に関連付けられる種類のプロジェクトです。
したがって、イベント ソーシングが小規模なプロジェクトで使用されることはめったにありません。多くの開発者が関わっているプロジェクトであり、私のブログの定期的な読者の多くが取り組んでいるプロジェクトです。
その理由は、私たちがイベント ソーシングについて考えている根本的な誤りによるものだと思います。
私がイベント ソーシングについて議論するとき、多くの人は、イベント ソーシングにはかなりのオーバーヘッドが伴い、そのオーバーヘッドは小規模なプロジェクトでは正当化されないと思い込んでいます。 彼らが言うには、イベント ソーシングが実際にコストを削減するプロジェクトの範囲によって決定される、ある種のピボット ポイントがあり、小規模なプロジェクトではコスト オーバーヘッドが導入されるということです。
このようなもの:

もちろん、これは単純化しすぎていますが、この議論は非常によく視覚化されています。イベント ソーシングは、価値があると確信しているプロジェクトでのみ使用する必要があります。
この声明の問題点は、それについて話していないことです ただ イベントソーシング、それはイベントソーシングについて話します と 関連するすべてのパターン: 集約、サガ、スナップショット、シリアライゼーション、バージョン管理、コマンドなど
そのため、グラフは次のようになります。

しかし、その上に構築されたパターンなしでイベント ソーシングを使用することは理にかなっていますか? これらのパターンが存在するのには十分な理由があります。 それが今日私が答えたい質問です。
以下は、イベント ソーシングとは何かについての Martin Fowler のビジョンです。
「アプリケーションの状態を照会して、世界の現在の状態を調べることができます。これにより、多くの質問に答えることができます。しかし、自分がどこにいるのかだけでなく、どのようにしてそこにたどり着いたのかを知りたい場合もあります.
イベント ソーシングにより、アプリケーションの状態に対するすべての変更が一連のイベントとして確実に保存されます。 これらのイベントを照会できるだけでなく、イベント ログを使用して過去の状態を再構築し、遡及的な変更に対処するために状態を自動的に調整する基盤としても使用できます。
イベント ソーシングの基本的な考え方は、アプリケーションの状態に対するすべての変更がイベント オブジェクトにキャプチャされ、これらのイベント オブジェクト自体が、アプリケーションの状態自体と同じ有効期間に適用された順序で格納されるようにすることです。”
言い換えれば、イベントソーシングは、結果ではなく、変更を保存することです。 プロジェクトの最終的な状態を構成するのは、これらの変更です。
イベント ソーシングでは、「カートがチェックアウトされているか」という質問は、カートのステータスではなく、そのカートに関連して発生したイベントを調べることによって回答する必要があります。
それは確かにオーバーヘッドのように聞こえますが、そのようなアプローチの利点は何でしょうか? ファウラーは次の 3 つを挙げています。
完全な再構築: イベントを確認するだけで、アプリケーションの状態を破棄して再構築できます。 これにより、プログラム フローやデータ構造が将来変更されることがわかっている場合に、大きな柔軟性が得られます — この記事の後半でこの例を示します。
時間クエリ: 実際にイベント自体をクエリして、過去に何が起こったかを確認できます。 起こったことの最終結果だけでなく、イベント自体のログもあります。
イベントリプレイ注: 必要に応じて、イベント ログを変更して間違いを修正し、その時点からイベントを再生して正しいアプリケーション状態を再構築できます。
ただし、Fowler のリストには重要なことが 1 つ欠けています。それは、イベントが時間を非常にうまくモデル化することです。 実際、それらは CRUD よりも人間が世界を認識する方法にはるかに近いものです。
あなたの日常生活のあるプロセスを考えてみてください。 それはあなたの朝の日課かもしれませんし、食料品を買っているかもしれませんし、授業に出席したり、職場で会議をしたかもしれません。 いくつかのステップが含まれている限り、何でも構いません。
次に、そのプロセスをできるだけ詳しく説明してください。 例として、朝の日課を取り上げます。
私は午前5時に起きました
歯を磨きました — 歯の衛生は重要です — そして服を着ました
コーヒーを作るために階下に行った
ホームオフィスに行きました(コーヒーを飲みながら)
メールを読む
この投稿を書き始めました
CRUD アプローチのように、現在起こっていることの状態を含むテーブルを持つよりもはるかに自然です。
私の朝のルーティーンのような平凡なものに発見される「時間の流れ」がすでにあるとすれば、クライアントのプロジェクトのプロセスはどうなるでしょうか。 予約の作成、請求書の送信、在庫の管理、名前を付けます。 「時間」は非常に重要な側面であることが多く、CRUD は現在の状態しか表示しないため、時間の管理にはあまり適していません。
どのような種類のフレームワークやインフラストラクチャも必要とせずに、イベント ソーシングが非常に単純であり、CRUD と比較してオーバーヘッドがなく、実際にはオーバーヘッドがないことを示す例を紹介します。
私はブログを持っています。 Google アナリティクスを使用して、訪問者やページ ビューなどを匿名で追跡しています。もちろん、Google がプライバシーに最も重点を置いているわけではないことはわかっているので、別の方法をいじくり回していました。 ある日、クライアント側のトラッキングに頼るのではなく、サーバー ログだけを頼りにして、アクセスされたページの数を特定できないかと考えました。
そこで、nginx のアクセス ログを監視する小さなスクリプトを作成しました。 ボットやクローラーなどのトラフィックを除外し、各訪問をデータベース テーブルに 1 行として保存します。 このような訪問には、URL、タイムスタンプ、ユーザー エージェントなどのデータが関連付けられています。
以上です。
ああ、あなたはもっと期待していましたか? まあ、私は自分の生活を楽にするためにもう少しコードを書くことになりましたが、本質的に、ここにすでにあるのはイベントソーシングです: アプリケーションで起こったすべての時系列ログを保持し、SQL クエリを使用して集計できますたとえば、1 日あたりの訪問数を表示するデータ。
もちろん、時間の経過とともに何百万ものアクセスがあるため、生の SQL クエリを実行するのは面倒になる可能性があるため、イベント ソーシングに基づくパターンを 1 つ追加しました。 CQRS の「読み取りモデル」とも呼ばれます。
訪問をテーブルに保存するたびに、それをイベントとしてディスパッチします。 それらを処理するサブスクライバーがいくつかあります。たとえば、1 日あたりの訪問をグループ化するサブスクライバーがあり、2 つの列を持つテーブルでそれらを追跡します。 day と count. 文字通り、ほんの数行のコードです。
class VisitsPerDay
public function __invoke(PageVisited $event): void
DB::insert(
‘INSERT INTO visits_per_day (`day`, `count`)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE `count` = `count` + 1’,
[
$event->date->format(‘Y-m-d’),
1,
]
);
毎月の訪問をご希望ですか? URLごと? 新しい購読者を作るだけです。 ただし、キッカーは次のとおりです。アプリケーションのデプロイ後にそれらを追加し、以前に保存されたすべてのイベントを再生できます。 そのため、プロジェクターを変更したり、新しいプロジェクターを追加したりした場合でも、新しい機能を展開したときだけでなく、イベントの保存を開始した時点からいつでも再生できます。
これは最初のうちは特に役に立ちました。実際のユーザーではないボットやトラフィックである大量のデータが入ってきました。 スクリプトを数日間実行し、結果を観察し、フィルタリングを追加し、すべての予測データを破棄して、すべての訪問をもう一度再生しました。 これにより、データを変更するたびに最初からやり直す必要がなくなります。
そして、このイベント ソース プロジェクトのセットアップにかかった時間を推測できますか? 開始から実稼働バージョンまで 2 時間。 もちろん、DBAL とイベント バスのフレームワークを使用しましたが、特にイベント ソーシングに関連するものは何もありませんでした。 翌日、微調整を行い、投影表に基づいていくつかのチャートを追加しました。 しかし、イベント ソーシングのセットアップは非常に簡単に構築できました。
ここで私が言おうとしているのは、イベント ソーシングの考え方は、多くの種類のプロジェクトで非常に強力であるということです。 20 人の開発者チームが 5 年間取り組む必要があるものだけではありません。 「時間」が重要な役割を果たす問題は数多くありますが、それらの問題のほとんどは、オーバーヘッドをまったく発生させずに、非常に単純な形式のイベント ソーシングを使用して解決できます。
実際、CRUD アプローチを使用していたら、この分析プロジェクトを構築するのにもっと多くの時間がかかっていたでしょう。 変更を行うたびに、この変更が実際の訪問で効果的であることを確認するために数日待たなければなりませんでした. イベント ソーシングにより、1 人の開発者である私は、実際にははるかに生産的になりました。 これは、イベント ソーシングで実現できると多くの人が信じていることとは正反対です。
さて、誤解しないでください。 イベント ソーシングが複雑なドメインの問題を単純化すると言っているわけではありません。 複雑なプロジェクトは、イベント ソーシングの有無にかかわらず、多くの時間とリソースを必要とします。 イベント ソーシングは、これらのプロジェクト内の一部の問題を単純化し、他の問題を少し難しくします。
ただし、イベント ソーシングを使用するかどうかの絶対的なコストについて結論を出すつもりはないことを覚えておいてください。 イベント ソーシング自体は極端に複雑である必要はなく、比較的小規模なプロジェクトであっても、実際により良いソリューションになる可能性があることを人々に認識してもらいたいだけです。
10 年以上前に「イベント ソーシング」という用語を思いついた Greg Young 氏。 イベント ソーシングの出発点がフレームワークである場合、それは間違っていると述べています。 インフラストラクチャを必要としないのは、まず心の状態です。 私は実際、より多くの開発者がこのように考えて、最初に複雑さの精神的な層を取り除き、実際に必要なときにのみそれを追加することを望んでいます. 特別なフレームワークがなくても、今日からイベント ソーシングを開始できます。実際にワークフローが改善される可能性があります。
あなたがここまでたどり着いたのなら、あなたは私が書いたものに興味をそそられたと思います — あるいは、そのために非常に怒っているでしょう. いずれにせよ、私が作成している PHP コミュニティ向けのコースを見ていただきたいと思います。 その中で私はイベントソーシングを一から教え、非常に基本的なことから始めますが、大規模なプロジェクトの複雑なパターンもカバーします. これは Laravel の Event Sourcing と呼ばれ、巨大なプロジェクトだけでなく、あらゆる種類のプロジェクトでのイベント ソーシングへの非常に実用的なアプローチを教えてくれます。
また、私のコンテンツに関する最新情報を入手したい場合は、ニュースレターの購読を検討してください。私が取り組んでいることについてメールを送信することがあります。
最後に、さらに詳しく知りたい場合は、脚注をご覧ください。