簡介
事件溯源和 CQRS 是兩種強大但經常被濫用的模式。本文將幫助您了解它的本質、真正的好處,最重要的是—何時不使用它。

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 缺點
- 複雜性:需要事件儲存、預測、快照
- 最終一致性:讀取模型不是即時的
- 模式演化:改變事件模式非常困難
- 困難查詢:無法 SELECT * FROM 訂單 WHERE status = 'SHIPPED'
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
總結
| 圖案 | 複雜性 | 何時使用 | 何時避免 |
|---|---|---|---|
| 簡單的增刪改查 | 低 | 大多數服務 | 高流量閱讀 |
| 僅限 CQRS | 中 | 獨立的讀取/寫入秤 | 簡單域 |
| 事件溯源 | 高 | 審計追踪,時間旅行 | 簡單的增刪改查 |
| ES + CQRS | 非常高 | 財務、訂購 | 幾乎所有其他 |
黃金法則: 從簡單開始(CRUD)。僅當特定的痛點證明有必要時才添加 CQRS/ES。
下一篇文章: 第 10 課:什麼是微前端? — 效益、權衡與決策框架