イベントソーシング
状態の変更をイベントとして記録し、イベントの再生で現在の状態を復元する設計パターン
設計パターンイベント駆動
イベントソーシングとは
イベントソーシングは、状態の変更をイベントとして記録し、イベントの再生 (リプレイ) で現在の状態を復元する設計パターンである。CRUD の「更新」ではなく「追記」でデータを管理する。
CRUD vs イベントソーシング
CRUD:
UPDATE accounts SET balance = 900 WHERE id = 'A';
→ 以前の値 (1000) は失われる
イベントソーシング:
Event 1: AccountCreated { id: 'A', balance: 1000 }
Event 2: MoneyWithdrawn { id: 'A', amount: 100 }
→ 現在の状態: balance = 1000 - 100 = 900
→ 全履歴が残る
DynamoDB でのイベントストア
// イベントの書き込み
await db.put({
TableName: 'events',
Item: {
entityId: 'order-123',
timestamp: Date.now(),
eventType: 'OrderCreated',
data: { items: ['item-1'], total: 1000 },
version: 1,
},
});
// 状態の復元 (イベントのリプレイ)
const events = await db.query({
TableName: 'events',
KeyConditionExpression: 'entityId = :id',
ExpressionAttributeValues: { ':id': 'order-123' },
ScanIndexForward: true, // 時系列順
});
const state = events.Items.reduce((order, event) => {
switch (event.eventType) {
case 'OrderCreated': return { ...event.data, status: 'created' };
case 'OrderPaid': return { ...order, status: 'paid' };
case 'OrderShipped': return { ...order, status: 'shipped' };
default: return order;
}
}, {});
メリット
全変更履歴がイベントとして残るため、完全な監査証跡が得られる。任意の時点の状態を復元する「時間旅行」が可能で、デバッグ時に問題の再現が容易になる。イベントが発生するたびに下流システムへ自然に通知できるため、イベント駆動アーキテクチャとの親和性が高い。
デメリット
| デメリット | 対策 |
|---|---|
| クエリが複雑 | CQRS で読み取り用ビューを分離 |
| イベント数の増加 | スナップショットで高速化 |
| スキーマの進化 | イベントのバージョニング |
CQRS との組み合わせ
書き込み: イベントストア (DynamoDB)
↓ DynamoDB Streams
読み取り: マテリアライズドビュー (別テーブル or OpenSearch)
ユースケース
| ユースケース | 理由 |
|---|---|
| 金融取引 | 監査証跡が必須 |
| 注文管理 | 状態遷移の履歴が重要 |
| ゲーム | リプレイ機能 |
| コラボレーション | 変更の競合解決 |
全体像を把握するには関連書籍も有用。