🗣️

イベントソーシングについて OSC 2024 Online/Spring で話した

2024/03/06に公開

2024年3月5-6日、弊社ジェイテックジャパンも協賛の Open Source Conference 2024 Online/Spring が開催されました。弊社 CTO の高丘さんと私も登壇し、イベントソーシングについて、また弊社が開発し OSS として公開しているイベントソーシング / CQRS 用の開発フレームワーク Sekiban について紹介しました。セッションの録画が YouTube で公開されていますので、興味のある方はぜひご覧ください。45分と少し長いセッションなので、特にポイントとなる点をこの記事にまとめました(以下、各小見出しがリンクになっていますので、そこから動画の該当部分にとべます)。
https://www.youtube.com/watch?v=kub7-6ww4H8

セッションの概要

セッションのタイトルは、「貴重なデータ、捨ててませんか?~ OSSで始めるイベントソーシングのススメ」です。2つのセクションがあります。セクション1では、イベントソーシングが何か、どんなメリットがあるのか、あまり詳しくない人でも分かりやすいように説明することを目指しました。セクション2では、Sekiban によって簡単にイベントソーシングを始められることを紹介しています。

セクション1: イベントソーシングって何?

ステートとイベント - 局面図と棋譜

多くのアプリケーションでは、データの状態をデータストアに永続化します(ステートソーシングと呼びます)。これに対して、データの状態ではなく状態の遷移、つまりどんな変更があったかをイベント履歴という形で永続化するのがイベントソーシングです。両者は、将棋(或いは囲碁やチェスなどのボードゲーム)の局面図と棋譜に例えられます。

局面図は、対局の現在の(あるいは示されている手までの)状態データです。見て分かりやすい反面、過去の状態、つまりどのような手順で対局が進んできたか、を知ることはできません。

一方の棋譜は、対局の履歴データです。すべての指し手が記録されているので、棋譜上の記録を初手からたどれば任意の手までの局面を盤面上に再現できる、という特徴があります。それは逆に、局面つまり状態を知りたければ必ず手順をたどらないといけない、という弱点でもあります。

ステートソーシングとイベントソーシング、それは即ち、対局データとして局面図を保存することと棋譜を保存することに相当します。注目したいポイントは、棋譜からは局面図を起こせるが、一枚の局面図からは棋譜を起こせない、という点です。それで、棋譜には対局のすべてが記録されると言えます。

ステートソーシングのデータ処理

ステートソーシングの場合、どのようにデータが処理され保存されるか、見ていきます。この図の例では、コーヒーというアイテムが一つある買い物カートのステートを読み出し、紅茶を追加するという変更を加えて、コーヒーと紅茶という二つのアイテムを持った新たなステートをDBに保存しなおす、つまり元々あったステート情報を新しいステート情報で上書きします。

このようにステートソーシングでは、処理の結果として得られる状態つまりステートデータを読み書きします。ステートソーシングのアプリケーションは、データの参照と更新のどちらにも同じ型のデータモデルを使える、そして実際そのように実装されることが多い、という特徴があります。

イベントソーシングのデータ処理

続いて、イベントソーシングの場合のデータ処理です。イベントソーシングの場合、買い物カートに加えられてきた変更がイベント履歴として保存されています。しかしユーザーが参照したいのはイベント履歴ではなく買い物カートの状態つまりステートなので、イベント履歴を再生することで現在のステートを生成します。参照時に関しては、フロントエンドに渡すデータはステートソーシングと変わりありません。一方更新時は、更新したステートではなく、変更したい内容をコマンドとしてバックエンドに送ります。バックエンドがコマンドを処理すると新たなイベントが発生しますので、それをイベント履歴に追記します。

このように、通常イベントソーシングでは参照系と更新系のデータモデルと処理が非対称となります。

上の図の事例の場合、アプリケーションの注文処理に必要なのは、ユーザーが最終的に注文したアイテムのリスト、つまりステートです。そのデータを取得できるという観点では、ステートソーシングでもイベントソーシングでも違いはありません。そして図が示す通り、イベントソーシングの実装はステートソーシングと比べて複雑になりやすい傾向があります。そうするとイベントソーシングにする理由がなさそうですが、もちろんそんなことはありません。イベントソーシングのメリットの一つは、アプリケーションの今の要件を満たすためには不要でも潜在的には価値のあるデータを記録しておけることです。

ステートソーシングでは失われるデータ

この例を見ると、ステートソーシングの場合にある種のデータが失われることが分かります。ユーザーがこのような手順で買い物カートに変更を加えていった場合、最終的に注文されなかったジュースの情報は保存されません。しかし、ユーザーがジュースをカートに入れたというデータは、アプリケーションの注文処理には不要でも、ビジネス上は価値を持っている可能性があります。例えば、次回の買い物時にサジェストをするために役立てられます。

それなら、買い物カートの状態が変わる度にそのステートをすべて保存しておけばよい、という考え方もあります。実際それはアプリケーション開発でよく用いられる手法の一つです。しかしそれでもなお、イベントソーシングに比べて不利な点があります。

  1. どう変更されたかを知るには、2つのステートを比較し差分を取らなくてはいけない。
  2. 差分が取れても、なぜその変更が生じたかの理由は記録されていない。

イベントソーシングではどうなる?

イベントソーシングの場合、いったんは買い物カートに入れて後から除いたジュースの情報も保持されます。重要なのは、イベントソーシングではイベント履歴を変更も削除もせず、追記だけ行う、という点です。そして、イベントは変更の理由そのものを示すデータです。それで、買い物カートに加えられたすべての変更とその理由の履歴が保持されることになります。これは、後のデータの利活用、また監査・デバッグ時のデータ分析をするのに有利になります。

ステートソーシングとイベントソーシング、それぞれのメリット・デメリット

セッションのこの部分で、ステートソーシングとイベントソーシングのメリット・デメリットをまとめています(当然ですが、ここで触れていない点もあります)。

ここで解説しているもう一つのイベントソーシングの特徴が、ステートソーシングと比べてデータの改ざんがしづらい、という点です。イベント履歴を改ざんする場合、前後のイベントとの整合性を取る必要があるからです。この点は、将棋の局面図と棋譜、どちらの改ざんが容易か考えてみると、イメージしやすいです。しかし改ざんのしづらさは裏を返せば、DBに直接アクセスしてデータを変更するといったメンテナンスも困難、ということでもあります。

いずれにせよ、ステートソーシングとイベントソーシング、それぞれにメリット・デメリットがあります。どちらかが絶対的に優れているということはないはずで、ケースごとに相応しいものを選択すべきです。

イベントソーシングと CQRS

イベントソーシングは CQRS と一緒に使われることが一般的です。本セッションの目的と時間の都合で詳しくは扱いませんでしたが、イベントソーシングと CQRS の相性が良い理由について簡単に触れました。コマンドがイベントを生じさせるという関係性があるので、この2つのパターンは組み合わせるとより使いやすくなります。

イベントソーシングは広く使われている?

現状、イベントソーシングは広く使われているとは言い難いと思います。考えられる一つの理由は、実装の難易度が高いことです。一からイベントソーシングを実装してアプリケーションに組み込むことは簡単ではありません。逆に言えば、そこを代わりにやってくれる使いやすいフレームワークがあれば、イベントソーシング導入の敷居は低くなるはずです。それで、私たちジェイテックジャパンは自分たちで使いやすいフレームワークを開発することにしました。それが、続くセクション2でご紹介する Sekiban です。

セクション2: Sekiban で始めるイベントソーシング

Sekiban の紹介と、Sekiban でできること

Sekiban は、C# で書かれたイベントソーシング / CQRS 用のフレームワークです。すべての機能をオープンソースソフトウェアとして公開しています(ライセンスは Apache 2.0)。

このフレームワークは、アプリケーション内でイベントソーシングと CQRS の構成要素(コマンド・クエリ・イベント・集約・プロジェクションなど)を、できるだけ簡潔かつ宣言的に定義できるようにします。

さらに、コマンドの実行とコマンド・イベントの自動保存、およびイベント履歴の再生(プロジェクション)とクエリの実行機能を持ちます。Sekiban を使えば、開発者は DB とのデータの読み書き(いわゆるインフラストラクチャ層)を実装する必要がなくなります。DB として Cosmos DB / DynamoDB / PostgreSQL を選択できます。データストアへの接続文字列を設定するだけで実行が可能です。

コマンドやクエリの実行を自動テストするためのライブラリや、Web API の自動生成用ライブラリも含んでいます。

Sekiban のデモ

Sekiban のデモです。デモに使ったコードはGitHubのリポジトリから参照できます。

Sekiban の現状と今後

Sekiban はまだ新しいフレームワークですが、運用中の幾つかの小規模なシステムですでに使われています。今後の目標は、より多くの環境で動かせるようにすること、また大規模なアプリケーションにも使えるようにすることです。

イベントソーシングという選択肢

プログラミング言語・アーキテクチャ・プラットフォームなど、ソフトウェアの開発にはいろいろな側面があります。そしてほとんどの場合、どの部分にも複数の選択肢があります。デザインパターンにしろアーキテクチャにしろ、銀の弾丸は存在しません。だからこそ、様々なケースに対応するためのツールをツールボックスに入れておく、つまり複数の選択肢を持っておいてケースによって使い分けることは、ソフトウェア開発の幅を広げることにつながります。私たちは、イベントソーシングもそのような役立つツールの一つになれると考えています。

使いやすいフレームワークがあればイベントソーシングは難しくありません。ぜひ Sekiban を通してイベントソーシングに触れてみてください。Sekiban に関する情報は以下の場所から入手していただけます。Sekiban に関してお尋ねになりたいことがありましたら、この記事のコメント欄に書いていただくか、Xアカウントにメッセージをいただければと思います。

X
https://twitter.com/sekibandev

GitHub
https://github.com/J-Tech-Japan/Sekiban

NuGet
https://www.nuget.org/profiles/J-Tech-Japan

ジェイテックジャパンブログ

Discussion