イベントソーシング

状態の変更をイベントとして記録し、イベントの再生で現在の状態を復元する設計パターン

設計パターンイベント駆動

イベントソーシングとは

イベントソーシングは、状態の変更をイベントとして記録し、イベントの再生 (リプレイ) で現在の状態を復元する設計パターンである。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)

ユースケース

ユースケース 理由
金融取引 監査証跡が必須
注文管理 状態遷移の履歴が重要
ゲーム リプレイ機能
コラボレーション 変更の競合解決

全体像を把握するには関連書籍も有用。

関連用語