イベントソーシングとは?状態再生とイベントストア設計を実装視点で解説【2026年版】

イベントソーシングは、データの現在値を上書き保存する代わりに、業務で起きた出来事を追記専用のストアへ順に積み、それを再生して現在の状態を組み立てる持ち方です。設計そのものは単純ですが、採用すると問い合わせの書き方・テストの当て方・個人データの消し方まで変わります。この記事では、イベント列から状態を復元する仕組み、KurrentDBやMartenといったイベントストアの選択肢と自前実装との差、CQRSを組み合わせたときの結果整合性の扱い、スキーマ進化・冪等性・スナップショットという運用で詰まる論点、そして受託開発の現場でどの業務に採用しどこで見送るかまでを、実装者の視点で整理します。

まとめ|イベントソーシングを採用する業務と見送る業務の判断軸

結論から書きます。イベントソーシングは全社的な設計方針ではなく、履歴そのものが業務価値を持つ限られた領域に部分適用する道具です。会計台帳、在庫の引当と解放、与信枠の増減、予約席の確保とキャンセル。後から「何が起きたか」を証明する必要のある業務では、監査証跡と過去時点の再現が副産物として手に入ります。

逆に、参照マスタや設定情報のように現在値だけ読めればよいデータ、寿命の短い実証段階のシステム、そしてチームにイベント駆動の運用経験がない状態での全面導入は見送る判断が妥当です。イベント設計・版管理・投影基盤への先行投資が回収できず、テストとデバッグの手数だけが増えます。

難易度は3点でほぼ決まります。イベントストアに何を使うか、読み取りをどう分けるか、過去イベントのスキーマ変更をどう吸収するか。以降の章でこの順に、製品名と版番号、実装手順まで落として説明します。

イベントソーシングの基本構造|追記専用ストアと状態再生の流れ

まず押さえるのは、記録の単位が「状態」ではなく「出来事」に変わる点です。イベント駆動の全体像はイベント駆動アーキテクチャの仕組みと実装パターンで整理しているため、ここでは永続化の側だけを扱います。

上書き保存との違い|行ロック競合と履歴喪失という2つの制約を避ける設計

従来の作成・読み取り・更新・削除の型では、更新のたびに読み取り、変更し、行をロックして書き戻します。同一エンティティへ同時に書き込みが集中すると、この読み取り・変更・書き込みの周期がそのまま待ち行列になります。予約締切直前の座席確保や、セール開始直後の在庫引当がその典型です。

もう一つの制約が履歴の喪失です。残席が42という値だけを保存すると、「2席予約された」のか「3席予約されて1席解放された」のかを後から区別できません。監査ログを別途仕込めば追えますが、業務データとログの二重管理になり、片方だけ欠ける事故が起きます。イベントソーシングは、この2つの制約を「更新しない」という一点で外します。

イベント設計の粒度|結果の状態でなく業務上の意図を記録する書き方

イベントには、変更後の値ではなく、変更を起こした業務上の意図を書きます。RemainingSeatsChangedTo42ではなくSeatsReservedに予約数と予約者を持たせる、という粒度です。前者は値の変更ログにしかならず、後から別の読み取りモデルを組み立てられません。

命名は過去形の他動詞で統一します。OrderPlacedPaymentCapturedShipmentCancelled。この形にそろえると、業務担当者がイベント一覧をそのまま読めるようになり、要件のずれが設計段階で見つかります。逆にOrderUpdatedのような曖昧な名前を1つ許すと、そこに複数の業務意図が混ざり、投影側で中身を場合分けする羽目になります。

リハイドレートの手順|ストリーム読み出しから現在状態の再構成まで

エンティティ1件につき1本のストリームを持ち、そこへイベントを追記します。現在状態が必要になったら、そのストリームを先頭から読み、順にイベントを適用して状態を組み立て直す仕組みです。この復元処理をリハイドレートと呼びます。

  1. コマンドが到着したら、対象エンティティのストリームを読み出す
  2. イベントを発生順に適用し、メモリ上に現在状態を構築する
  3. その状態に対して業務ルールを検証し、可否を判定する
  4. 通れば新しいイベントを、読み出し時点の版番号を条件に追記する
  5. 版番号が進んでいれば追記は拒否され、1に戻って再試行する

5の拒否は失敗ではなく、楽観的並行制御が働いた合図です。行ロックを取らずに競合を検出できるため、同一エンティティへの同時書き込みが多い業務でも待ちが発生しません。二重予約が起きないのは、状態を読んだ時点と書いた時点のずれをストアが検出するからです。

イベントストアの選択肢|専用製品と追記テーブル自前実装の差を見極める基準

イベントストアには、専用データベースを使う道と、手元のリレーショナルデータベースに追記専用テーブルを切る道があります。どちらでも動きますが、自前で書く量が大きく変わります。

KurrentDBとMarten|専用イベントストア製品の2026年8月時点の版

代表的な専用製品がKurrentDBです。EventStoreDBから改名された製品で、公式リポジトリのREADMEにも「EventStoreDB will be referred to as KurrentDB」と改称が明記されています。GitHubのリリース一覧では2026年8月4日時点の最新が26.1系(v26.1.1・2026年7月27日公開)でした。

.NET圏では、PostgreSQLをイベントストアとして扱うMartenが選択肢になります。同じく2026年8月4日時点で9.22系(V9.22.3・2026年8月3日公開)が公開されており、既存のPostgreSQL運用に乗せられる点が導入時の摩擦を下げます。Java圏ではAxon FrameworkとAxon Serverの組み合わせが定番で、こちらは5.2系が安定版(axon-5.2.2・2026年7月27日公開)、5.3系はRC段階という状況です。Javaでの具体的な実装手順はAxon FrameworkでCQRSとイベントソーシングを組む方法で扱っています。

選択肢 版(2026年8月4日時点) 自前実装が要る範囲
KurrentDB 26.1系 ほぼ不要
Axon Server Framework 5.2系 ほぼ不要
Marten(PostgreSQL) 9.22系 投影の運用設計
RDBの追記テーブル 製品の版に依存 並行制御・購読・スナップショット

版番号は執筆時点の実測値で、更新頻度が高い製品群です。採用検討では各リポジトリのリリース一覧で現況を確認します。

追記専用テーブルで組む場合に自前で書くことになる4つの機能と実装範囲

PostgreSQLやMySQLにイベント表を1つ作れば、保存自体はすぐ動きます。足りなくなるのは、専用製品が組み込みで持っている次の機能です。

  • ストリーム単位の順序付き読み出し(エンティティ識別子と連番の複合索引)
  • 楽観的並行制御(期待版番号と一致する場合のみ追記する一意制約)
  • 購読と再送(投影が止まった位置から再開するチェックポイント管理)
  • スナップショットの保存と読み込み

1件のエンティティに数十イベントしか積まれない業務なら、この自前実装は数百行で収まります。判断が変わるのは購読です。投影を別プロセスにする、複数の読み取りモデルを並走させる、障害後に途中から再開する。この3つが要件に入った時点で、専用製品や専用ライブラリに寄せた方が総手数は少なくなります。

Kafkaをイベントストアの代わりにできない理由と併用時の役割分担

実装前に必ず区別してください。Apache Kafkaはメッセージブローカーであり、イベントストアではありません。エンティティ単位のストリームを条件付きで読み出す機能も、追記時の楽観的並行制御もないため、リハイドレートと二重更新の検出を担えません。

併用する場合の役割は明確です。イベントストアが正本を持ち、そこに書かれたイベントを外部システムや投影へ配るところをブローカーが担います。Kafkaの設計思想と適性についてはApache Kafkaが選ばれる理由と特徴で解説しているので、配信基盤側の選定はそちらを参照してください。なお、粒度の細かい内部イベントをそのまま外部へ流すと結合が強くなります。外部連携には統合用のイベントを別に定義してください。

CQRSと投影の設計|結果整合性の遅延をどこまで許すかの判断

イベント列は書き込みに向いた形をしているため、一覧表示や集計をそのまま賄えません。そこで読み取り側に別のモデルを置きます。CQRSの定義と読み書きモデルの分離そのものはCQRSの仕組みとRead Model・Write Modelの実装パターンで扱っているため、ここではイベントソーシングと組んだときの固有論点に絞ります。

投影の更新経路と読み取り専用ストアの持ち方を決める業務上の判断軸

投影は、イベントを購読して読み取り用のテーブルやドキュメントを組み立てる処理です。更新経路は2通りあります。イベント追記と同一トランザクションで投影も更新する同期型か、購読して後から追いつく非同期型か。

同期型は遅延が生じない代わりに、投影を増やすほど書き込みの応答時間が伸び、投影の障害が書き込みまで巻き込みます。非同期型はその逆で、書き込みは軽くなりますが、画面に反映されるまでの時間差を業務側が受け入れることが前提です。実務では、当該画面の一覧だけ同期、分析用や通知用は非同期という混在配置に落ち着きます。読み取りモデルは後からいつでも作り直せます。イベント列が残っているかぎり、新しい投影を空から再生して埋められるためです。

結果整合性の遅延が業務要件に触れる場面と読み取り経路の分け方

非同期投影を採る場合、登録直後の再表示で自分の変更が見えない、という事象が必ず起きます。ここを「仕様です」と押し通すと現場が離れます。

回避策は、遅延が許されない読み取りだけ経路を分けることです。登録直後の確認画面はイベントストアから直接リハイドレートして表示し、一覧や検索は投影を使う。あるいは、コマンド応答に確定した版番号を返し、その版に投影が追いつくまで画面側で待つ。前者は実装が単純で、後者は待ち時間の上限管理が要ります。判断の分かれ目は、遅延中に別の操作へ進めるかどうかです。進めるなら経路分離、進めないなら版番号での待ち合わせを選びます。

イベントの版管理|スキーマ進化と補償イベントの扱い方を決める実務手順

イベントは不変です。保存済みのイベントを書き換えないという前提が、監査証跡と再生可能性を支えています。この前提を守ったままスキーマを変えるための手筋が、以下の3つです。

破壊的でない変更を吸収する寛容な読み手の実装と既定値の置き方

項目の追加のように後方互換な変更は、読み手側の設計で吸収します。逆シリアル化のときに未知の項目を無視し、欠けている項目には既定値を入れる。この作りにしておけば、過去イベントへ手を触れずに項目を増やせます。

既定値の置き方には注意が要ります。金額や数量に0を入れると、集計結果が静かに狂います。「値が無い」ことを表現できる型を使い、投影側で未設定の扱いを明示的に分岐させてください。追加した項目を必須として扱いたくなったら、それは後方互換な変更ではなく、次のアップキャスタの出番です。

アップキャスタの連鎖|過去イベントを書き換えず現行スキーマへ寄せる

項目の分割や意味の変更のような破壊的変更では、読み出し時に旧スキーマを現行スキーマへ変換する関数を挟みます。これがアップキャスタです。v1からv2、v2からv3と関数を連ねておけば、業務ロジックは常に最新の形だけを扱えます。

イベント本体には版識別子を持たせます。型名に含める方法(OrderPlaced.v2)と、封筒部分のメタデータに持たせる方法があり、後者の方が変換の分岐を書きやすい構成です。保存済みイベントを新スキーマへ書き直す一括移行は、不変性と監査証跡を壊すため最終手段に置きます。

補償イベントによる取り消し|誤った記録を消さずに打ち消す手順

誤ったイベントを追記してしまったときも、削除はしません。効果を打ち消す新しいイベントを追記します。SeatsReservedに対するReservationCancelledがその例で、原イベントは残り、取り消された事実も残ります。

厄介なのは不具合が生んだ誤りイベントです。コードを直しても過去のイベントは直らず、再生のたびに誤った状態が組み上がります。対処は、誤り分を打ち消す補償イベントを一括投入するか、アップキャスタで読み出し時に補正するかの二択です。前者では修正であることを封筒のメタデータに残し、監査時に業務イベントと区別できるようにします。

運用で詰まる3点|重複配信・再生コスト・個人データ削除への対処

設計が通っても、運用に入ってから顕在化する論点が3つあります。いずれも先に決めておかないと、後から入れるのが高くつきます。

少なくとも1回配信を前提にした冪等な消費者の作り方と検証手順

イベントの配信は通常「少なくとも1回」です。同じイベントが2回届く前提で消費者を作ります。届いた回数によらず結果が変わらない性質を冪等性と呼び、これが無いと投影がイベント列からずれ、決済や通知のような副作用が二重に起きます。

実装は2通りあります。消費者ごとに最後に処理したイベントの通し番号を保存し、それ以下は捨てる方式。もう一つは、状態変更そのものを繰り返し安全な形にする方式で、加算ではなく「この予約識別子の予約数を2にする」と書く形です。副作用を伴う消費者(メール送信・外部API呼び出し)では前者が確実です。処理済み識別子の保存と副作用を同一トランザクションに収められない場合は、送信前に識別子を記録し、重複送信より未送信を検知する側に倒します。

スナップショット間隔の決め方|保管コストと再生時間の釣り合い

ストリームが長くなると、リハイドレートのたびに全イベントを再生する時間が効いてきます。そこで一定間隔で状態を丸ごと保存し、以後は「直近のスナップショット+それ以降のイベント」だけを読みます。

間隔の決め方は、1件あたりの平均イベント数から逆算します。数十件で収まる注文のようなエンティティなら不要です。数千件に伸びる在庫や口座では、100件から500件おきという設定がよく使われます。判断材料は再生時間の実測で、コマンド応答の目標時間に対して再生が2割を超えたあたりが導入の目安になります。スナップショットは高速化の手段であって正本ではありません。壊れたら捨ててイベント列から作り直せる作りにしておきます。

個人データの削除要求と追記専用ストアの衝突を解く2つの方法の選定基準

削除しないという前提は、個人データの削除請求と正面から衝突します。イベントを物理削除するとストリームの整合性が壊れるため、設計段階で逃げ道を用意しておきます。

基本は、個人データをイベントストアの外に置き、イベントには識別子だけを持たせる分離です。これなら参照先を消すだけで済み、イベント列に手を触れずに削除要求へ応えられます。分離できない場合は暗号シュレッディングを使います。対象者ごとの鍵で個人データ項目を暗号化しておき、削除時に鍵を破棄する。イベントの構造は残したまま中身だけ復元不能になります。ただし読み書きのたびに暗号処理が挟まり、鍵の管理基盤が別途必要です。個人データを扱う業務では、この判断を実装開始前に済ませてください。後から入れると既存イベントの全件移行が発生します。

採用してよい業務と見送るべき業務|受託開発の現場で引く線の判断基準

ここまでの実装コストを踏まえて、判断を言い切ります。イベントソーシングは、履歴が業務価値に直結する領域に限って採用し、それ以外では使いません。

採用条件|台帳・在庫引当・与信のように履歴自体が価値になる領域

採用してよいのは、次の条件のうち2つ以上を満たす業務です。過去時点の状態を再現する要件がある(監査対応・遡及計算)。同一エンティティへの同時書き込みが競合する(座席・在庫・枠の確保)。何が起きたかを業務側が説明する義務を負う(会計・与信・医療記録)。

会計台帳が分かりやすい例です。仕訳は本来が追記であり、訂正も赤伝という補償イベントで表現します。業務の記録方法とイベントソーシングの構造が最初から一致しているため、設計上の無理がありません。在庫引当も同様で、引当と解放を出来事として積めば、締め時点の残数を何度でも再計算できます。

見送る条件|参照マスタ・短命な実証・全社一括導入の3パターン

逆に、次の3つでは採用しません。1つ目は参照マスタや設定情報です。変更頻度が低く履歴に価値が無い領域では、イベントストアの運用負荷が回収できません。2つ目は寿命が読めない実証段階のシステムです。イベント設計とスキーマ進化の戦略に先行投資しても、その前に作り直しが来ます。

3つ目が全社一括導入です。イベント駆動の運用経験がないチームがシステム全体へ広げると、テスト・デバッグ・障害対応の作法が同時に変わり、開発速度が落ちます。基礎知識のないまま採用した場合、監査証跡という利点を得る前に、投影のずれと重複配信の調査に時間を取られる結果です。この3パターンでは、上書き保存に監査ログを添える構成の方が総コストは低く収まります。

部分適用の設計|業務の境界単位で切り分ける実装の進め方と確認項目

採用する場合も、全体に広げる必要はありません。支払台帳や注文処理のように利点が大きい部分だけをイベントソーシングにし、利用者プロフィールや設定管理は上書き保存のまま残す。この混在は破綻せず、境界をまたぐ連携はイベントか同期APIで橋渡しします。

進め方は、まず境界の内側でイベント設計と投影を1つ完成させ、外部との連携インターフェースを固めてから隣の領域へ広げる順です。基幹システムや外部サービスとの接続部分の設計は、既存資産の制約に左右されます。API開発・システム連携は、この種の境界設計と接続方式の選定を含めて開発を請け負うサービスです。既存システムを止めずに一部だけイベント駆動へ寄せる進め方の相談も受け付けています。

よくある質問

イベントソーシングの検討でよく挙がる質問を、実装判断の観点でまとめます。

イベントソーシングとCQRSは必ずセットで使うものですか?

必須ではありませんが、実務ではほぼ同時に採用されます。イベント列は一覧表示や集計に向かないため、読み取り用のモデルを別に持つ構成が自然に必要になるからです。逆にCQRS側は単独で成立し、上書き保存のデータベースに読み書きのモデルを分けるだけの構成もあります。小規模なら、投影を同期更新にして「読み取り専用テーブルが1本ある」程度の実装から始めるのが現実的です。

イベントソーシングの最大のデメリットは何ですか?

問い合わせの自由度が落ちる点です。イベント列に対してSQLの条件検索に相当する操作はできず、取り出せるのは識別子を条件にしたストリームだけになります。任意条件の検索や集計は、すべて投影を作って賄う前提になるため、要件が増えるたびに投影の設計と再生が発生する構造です。次いで重いのがスキーマ進化で、過去イベントを書き換えられない制約の下でアップキャスタを保守し続ける負担が残ります。

イベントストアにはどのデータベースを使えばよいですか?

投影が1つで、購読の再開要件が無いなら、手元のPostgreSQLやMySQLに追記専用テーブルを作れば十分です。投影を複数並走させる、別プロセスから購読する、障害後に途中から再開する、といった要件が入るならKurrentDBやAxon Serverのような専用製品、.NET圏ならMartenを検討してください。判断軸は保存性能ではなく、購読と並行制御をどこまで自前で書く覚悟があるかです。

既存の上書き保存のシステムから移行できますか?

システム全体の一括移行は勧めません。現在値しか残っていない既存データからは過去のイベントを復元できないため、移行時点の状態を初期イベントとして1件記録し、以降の変更からイベントで積む形になります。まず境界の明確な業務を1つ選び、そこだけをイベントソーシングで作り直して既存システムとは連携で結ぶ。この単位なら、失敗しても戻せる規模に収まります。

イベントソーシングのテストはどう書けばよいですか?

過去イベントを与え、コマンドを実行し、発生したイベントを検証する形式が基本です。データベースやキューを起動せずに業務ロジックを検証できるため、単体テストは上書き保存の実装より書きやすくなります。増えるのは統合テストの側で、投影の整合、重複配信に対する冪等性、アップキャスタを通した過去イベントの読み出し、この3点は実際のストアを使って確認する必要があります。

関連記事

資料請求

RELATED POSTS 関連記事