はじめに
イベント ソーシングと CQRS は 2 つの強力なパターンですが、悪用されることが多い パターンです。この記事は、その性質、実際の利点、そして最も重要なことである 使用すべきではない場合 を理解するのに役立ちます。

1. イベントソーシング
1.1 中心となるアイデア
現在の状態を保存する代わりに、発生した一連のイベントを保存します。
Traditional (State-based):
┌─────────────────────────┐
│ orders table │
│ id: 123 │
│ status: SHIPPED ← chỉ biết trạng thái hiện tại
│ total: 500.000 │
└─────────────────────────┘
Event Sourcing:
┌─────────────────────────────────────┐
│ Event Store (Order #123) │
│ 1. OrderCreated {items, total} │
│ 2. PaymentReceived {amount} │
│ 3. ItemRemoved {itemId} ← biết toàn bộ lịch sử
│ 4. OrderConfirmed {} │
│ 5. OrderShipped {trackingId} │
└─────────────────────────────────────┘
State = replay(events) → current state
1.2 利点
- 完全な監査証跡: 誰がいつ何をしたかを正確に把握できます
- タイムトラベル: いつでも状態を再構築できます
- デバッグ: イベントをリプレイしてバグを再現します。
- 分析: イベント パターンを分析します。
1.3 欠点
- 複雑さ: イベント ストア、プロジェクション、スナップショットが必要
- 結果整合性: 読み取りモデルはリアルタイムではありません
- スキーマの進化: イベント スキーマの変更は非常に困難です
- 難しいクエリ: ステータス = '発送済み' の注文から * を選択することはできません
2. CQRS (コマンドクエリ責任分離)
2.1 読み取りと書き込みの分離
Traditional:
┌──────────┐ ┌──────────┐
│ Client │────►│ Service │────► Database
│ │◄────│(CRUD all)│◄──── (1 model)
└──────────┘ └──────────┘
CQRS:
┌──────────────┐ ┌────────────┐
────► │ Command Side │────►│ Write DB │
┌──────────┐ │ (Create, │ │ (optimized │
│ Client │ │ Update) │ │ for write)│
└──────────┘ └──────────────┘ └─────┬──────┘
────► ┌──────────────┐ │ Events
│ Query Side │ ┌─────▼──────┐
◄──── │ (Read, │◄────│ Read DB │
│ Search) │ │ (optimized │
└──────────────┘ │ for read) │
└────────────┘
2.2 CQRS が価値があるのはどのような場合ですか?
- 読み取り/書き込み比率 大きく異なります (読み取り 90%、書き込み 10%)
- 読み取りモデルには 別の形式 書き込みモデルが必要です (非正規化ビュー)
- 読み取り/書き込みを独立してスケーリングする必要がある(リードレプリカを追加する)
- 複雑なクエリには 最適化された読み取りモデル が必要です (Elasticsearch、マテリアライズド ビュー)
3. 意思決定の枠組み
3.1 イベント ソーシング + CQRS をいつ使用するか?
✅ 金融システム (監査証跡が必要) ✅ 注文処理(複雑なステータス、ニーズ履歴) ✅ 共同編集 (競合の解決) ✅ 規制遵守(何が起こったかを証明する必要がある)
3.2 使用すべきでない場合は?
❌ 単純な CRUD (ブログ、ユーザー プロファイル) → やりすぎ ❌ チームが小さく、経験がない → 学習曲線が高すぎる ❌ 証跡やタイムトラベルを監査する必要はありません ❌ リアルタイムの一貫性が必要です
3.3 イベント ソーシングなしで CQRS を使用できる
CQRS without Event Sourcing (pragmatic approach):
Write Side: PostgreSQL (normalized)
→ On write: publish event to Kafka
Read Side: Elasticsearch (denormalized)
→ Consumer: listen events → update search index
Đơn giản hơn nhiều, vẫn có lợi ích tách read/write.
4. 電子商取引への申請
Product Service: CQRS (no Event Sourcing)
Write: PostgreSQL (products table)
Read: Elasticsearch (search index, facets)
Sync: ProductUpdated event → ES consumer
Order Service: Event Sourcing + CQRS (trạng thái phức tạp)
Event Store: PostgreSQL (events table)
Read Model: PostgreSQL (materialized orders view)
Cart Service: Simple CRUD (Redis)
Không cần CQRS — read/write model giống nhau
User Service: Simple CRUD (PostgreSQL)
Không cần CQRS — straightforward CRUD
概要
| パターン | 複雑さ | いつ使用するか | 避けるべき場合 |
|---|---|---|---|
| シンプルな CRUD | 低い | ほとんどのサービス | 高トラフィックの読み取り |
| CQRS のみ | 中 | 個別の読み取り/書き込みスケール | 単純なドメイン |
| イベントソーシング | 高 | 監査証跡、タイムトラベル | シンプルな CRUD |
| ES + CQRS | 非常に高い | 財務、発注 | その他ほぼすべて |
黄金律: シンプル (CRUD) から始めてください。 CQRS/ES は、特定の問題点が必要であることが判明した場合にのみ追加してください。