イベントストア(Event Store)はイベントソーシングの心臓部とも言うべき永続化層であり、その設計品質がシステム全体の信頼性・パフォーマンス・保守性を大きく左右します。
イベントストアはすべてのドメインイベントを永続的に保存するAppend-onlyなデータストアであり、バージョニング・スキーマ進化・パーティショニング・検索機能など多くの設計上の考慮点があります。
本記事では、イベントソーシングのイベントストアとは何か、設計と管理方法、永続化層・バージョニング・スキーマ進化・パーティショニング・検索機能などについて解説していきます。
イベントストアはAppend-onlyな永続化層でありイベントの不変性が最重要原則
それではまず、イベントストアの基本設計と不変性の原則について解説していきます。
イベントストアの最重要設計原則は「Append-only(追記のみ)」です。イベントストアに書き込まれたイベントは削除も変更もされず、新しいイベントが追記されるのみという不変性(Immutability)がイベントソーシングの根幹をなします。
この不変性によって、完全な変更履歴・監査ログ・タイムトラベルという特性が保証されます。
イベントストアのデータモデル設計
イベントストアの基本的なデータモデルは以下のような構造になります。
イベントストアテーブルの例(PostgreSQL):
CREATE TABLE event_store (
id BIGSERIAL PRIMARY KEY, — 全体の通し番号
aggregate_id UUID NOT NULL, — 集約の識別子
aggregate_type VARCHAR(255) NOT NULL, — 集約の種類
event_type VARCHAR(255) NOT NULL, — イベントの種類
event_data JSONB NOT NULL, — イベントのデータ
event_version INT NOT NULL, — 集約内のバージョン
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(aggregate_id, event_version) — 楽観的ロック用
);
aggregate_idとevent_versionの複合ユニーク制約が楽観的ロックの実装に使われ、同時更新によるバージョン競合を防ぎます。
バージョニングと楽観的ロック
イベントストアへの書き込み時には楽観的ロックを使って同時更新の競合を検知します。
新しいイベントを追記する際に「現在のバージョン(expectedVersion)」を指定し、実際のバージョンと一致しない場合はConcurrencyExceptionを発生させます。
楽観的ロックは悲観的ロック(排他ロック)と異なりロックを取得しないため、高い並行処理性能を維持しながら整合性を保てます。ただしリトライロジックの実装が別途必要です。
スキーマ進化(Schema Evolution)の管理
長期運用するシステムでは必ずイベントのスキーマ変更が発生します。
スキーマ進化の主なアプローチとして以下があります。
Upcasterパターンは、古いバージョンのイベントを新しいバージョンに変換する変換器(Upcaster)を実装するアプローチです。
バージョンフィールドを持つイベントに対して、バージョンごとのUpcasterをチェーンで適用します。
WeakSchemaアプローチは、JSONやAvroなどの柔軟なスキーマ形式を使い、フィールドの追加は常に後方互換とし、フィールドの削除は非推奨マーキング後に段階的に行います。
イベントストアのパーティショニングと検索機能
続いては、大規模システムでのイベントストアのパーティショニング戦略と検索機能の実装方法を確認していきます。
パーティショニング戦略
イベントデータが大量になると、単一テーブルへのアクセス性能が低下します。
PostgreSQLのパーティショニング機能を使って集約タイプ別またはタイムスタンプ(月別等)でパーティション分割することが有効です。
Kafkaをイベントストアとして使う場合は、トピックのパーティション分割(aggregate_idのハッシュによるパーティション振り分け)が自然な形でスケーリングを実現します。
イベントストアの検索機能
イベントストアは基本的にaggregate_idによる全イベント取得を主要アクセスパターンとしますが、「特定イベントタイプの全イベント取得」「特定期間のイベント取得」などの横断的な検索が必要になる場合があります。
全文検索やイベントタイプでの検索には、ElasticsearchやOpenSearchにプロジェクションを作成して対応するアプローチが一般的です。
イベントストア自体に複雑なクエリ機能を持たせるよりも、CQRS的に読み取り用の検索インデックスを別途構築する設計が推奨されます。
EventStoreDBの特徴と利用
EventStoreDBはイベントソーシング専用に設計されたデータベースで、以下の特徴を持ちます。
ストリームベースのデータモデル(集約ごとにストリームを持つ)・ネイティブなProjection Engine(JavaScriptでプロジェクションを定義)・Persistent Subscriptions(信頼性の高いイベント配信)・組み込みのスナップショット機能・gRPCベースのAPIなど豊富な機能を備えています。
独自のDBを導入するコストはありますが、イベントソーシングに特化した機能がネイティブに使えるため、大規模システムでは検討する価値があります。
まとめ
本記事では、イベントソーシングのイベントストアとは何か、設計と管理方法、永続化層・バージョニング・スキーマ進化・パーティショニング・検索機能などについて解説しました。
Append-onlyという不変性の原則を守ったイベントストア設計が、イベントソーシングの特性(監査ログ・タイムトラベル)を保証する基盤となります。
楽観的ロックによる同時更新制御・Upcasterによるスキーマ進化管理・パーティショニングによるスケーリング・CQRS的な読み取りインデックスの分離という設計パターンを適切に組み合わせることで、長期的に運用可能なイベントストアを構築できます。
EventStoreDBなどの専用ソリューションとRDBMS(PostgreSQL等)の自前実装のどちらを選ぶかは、チームのスキルセットとシステム要件を総合的に判断して決定しましょう。