命令查詢職責分離 (CQRS) 與物化視圖

★★★★★Reviewed

FromCQRSDDIA

📚 From the Books

命令查詢職責分離 (CQRS) 與物化視圖CQRS & Event Sourcing

讀和寫是兩種不同的工作,憑什麼共用同一個模型?CQRS 把寫模型(command)與讀模型(query)拆開,各自針對自己的負載與形狀最佳化。代價是兩者之間多了一段最終一致的同步。

🧠 Intuition

TIP

一份正規化的寫模型保證資料正確;一份(或多份)去正規化的讀模型保證查詢快。寫模型負責「能不能改」,讀模型負責「好不好查」,中間用事件或投影把寫的變更同步到讀。

⚖️ Tradeoffs

為何要分 — 讀寫負載與形狀都不同
  • 多數系統讀遠多於寫,分開後可以各自獨立擴展(讀端加 replica、寫端保持單一致性源)。
  • 寫要的是正規化以維護不變量;讀要的是去正規化以避免昂貴 join。同一個模型同時滿足兩者,必然有一方被犧牲。
  • 分開後讀模型可以針對特定查詢量身打造,甚至用不同儲存引擎(寫進 RDBMS、讀走 Elasticsearch)。
物化視圖 / 讀投影 — 預先算好的答案
  • 讀模型本質是 materialized view:把 join、聚合、去正規化的結果事先算好落地,把成本從讀時搬到寫時。
  • 讀投影 (read projection) 透過訂閱寫端的事件流、增量更新自己,而不是每次全量重算。
  • 同一份事實來源可以投影出任意多個讀模型,需求變了就新增一個投影,不動寫端。
最終一致的讀端 — 必須面對的現實
  • 寫到讀有同步延遲,使用者可能寫完馬上讀卻看不到自己的更新(read-your-writes 問題)。
  • 緩解:UI 樂觀更新、寫完短暫讀寫端或 version 標記、明確告知使用者「處理中」。
  • 不能接受任何 staleness 的場景(強一致讀),CQRS 反而是負擔,別硬上。
與 Event Sourcing 的關係 — 常一起但不綁定
  • CQRS 和事件溯源是天作之合:事件流天然就是更新讀投影的來源,但兩者互相獨立
  • 可以只用 CQRS 不用事件溯源:寫端照常 CRUD,靠 CDC 或應用層發事件去更新讀模型。
  • 也可以只用事件溯源不嚴格分讀寫。把兩者當成必須綁定的套餐,是常見誤解。

🔑 Takeaways

✍️ My Notes

2026-07-01

  • Q: CQRS 聽起來很強,是不是預設就該把系統都拆成讀寫兩端?
  • A: 不是,這是最常見的過度使用陷阱。CQRS 引入了兩個模型、一條同步管線、以及最終一致性帶來的整套複雜度(延遲、read-your-writes、投影重建、監控同步落後)。只有當讀寫負載差異大、或讀的查詢形狀和寫的模型嚴重不匹配、需要獨立擴展或多種讀視圖時,這些成本才划算。對 CRUD 就能滿足的一般業務系統,單一模型遠比 CQRS 好維護。實務上更常見的是只在單一 bounded context 內、確實有讀寫不對稱的局部採用 CQRS,而不是整個系統一刀切。先用最簡單的單模型,等讀寫真的打架了再拆。

📖 Further Reading

🔗 Dive Deeper