事件溯源 (Event Sourcing)

★★★★★Reviewed

FromCQRSDDIA

📚 From the Books

事件溯源 (Event Sourcing)CQRS & Event Sourcing

不要儲存「現在的狀態」,而是儲存「導致這個狀態的每一個事件」。狀態變成衍生物:對事件序列做 fold 就能重建出來。難點不在寫事件,而在演化

🧠 Intuition

TIP

把它想成銀行帳本,而不是帳戶餘額。你從不直接改餘額,你只 append 一筆筆交易(事件);餘額是把所有交易加總算出來的。事件是 source of truth,狀態只是一個 cache。

⚖️ Tradeoffs

優點 — 完整審計與時間旅行
  • 每個狀態變化都有不可變紀錄,天生具備 audit log,金融/合規場景幾乎免費拿到。
  • 可重播到任一時間點的狀態(time travel),方便除錯、重現 bug、做事後分析。
  • 同一串事件可以重新投影出全新的讀模型——需求變了不必遷移舊資料,重播即可生成新視圖。
代價 — 查詢需要投影
  • 事件 store 只擅長「給我某個 aggregate 的事件流」;要做「所有未出貨訂單」這種查詢,必須先把事件投影 (projection) 成讀模型。
  • 這把複雜度推給了讀端:通常得搭配 CQRS,維護一份或多份物化的讀模型,且讀模型是最終一致的。
  • 直接對 raw event store 做 ad-hoc 查詢通常很痛,不要這樣用。
代價 — Schema 演化困難
  • 事件是不可變且永久保存的,第一版事件格式可能要被讀十年。
  • 演化手段:upcasting(讀取時把舊版事件轉成新版)、weak schema(容忍多餘/缺欄位)、版本化事件型別。
  • 千萬別「修改歷史事件」來改格式——那違反不可變性,也毀掉審計價值。要修正錯誤就 append 一筆補償事件 (compensating event)。
與 CDC 的差異 — 領域事件 vs 資料變更
  • CDC (Change Data Capture) 捕捉的是資料庫層的列變更(before/after row),是技術產物、由 DB 結構決定,語意低。
  • 事件溯源捕捉的是領域事件:帶業務意圖(CartAbandoned 而非「row 從 active 改成 inactive」),語意高、由領域建模產生。
  • CDC 是把既有 CRUD 系統的變更「事後抽出來」;事件溯源是一開始就以事件為事實來源。兩者都產生事件流,但抽象層級與意圖完全不同。

🔑 Takeaways

✍️ My Notes

2026-07-01

  • Q: 事件溯源和用 CDC(如 Debezium)把資料庫變更轉成 Kafka 事件,最後不是都得到一條事件流嗎?差在哪?
  • A: 差在事實來源的方向語意層級。CDC 系統裡,source of truth 仍是那張可變的資料表,事件是 DB 列變更的副產物,描述「欄位從 A 變成 B」,缺乏業務意圖、也無法回答「為什麼變」。事件溯源裡,事件本身就是 source of truth,狀態反而是衍生 cache,事件帶領域意圖(OrderCancelled 含取消原因),能完整重建「發生過什麼」。實務上 CDC 是改造既有 CRUD 系統、拿到變更流去餵下游的低成本路徑;事件溯源是從建模階段就承諾以事件為核心,成本更高但拿到完整審計與時間旅行。要審計與業務語意選事件溯源,只是要同步資料到下游選 CDC。

📖 Further Reading

🔗 Dive Deeper