DDD-CQRS的落地案例
摘要
在之前的文章DDD-CQRS能解什麼問題中,闡述了什麼是CQRS。但是並沒有業務需求可以應用CQRS。最近需要處理一個文本增量更新的業務,經過需求分析後,嘗試使用CQRS來解這個問題
問題分析
一個文本頁面編輯,對象很大,之前是全量保存。涉及到的網絡傳輸對象比較大,經常超時OOM,所以交互改成,只保存修改的部分,也就是增量更新。
之前業務中沒法使用CQRS,在於使用CQRS後,數據的維護變得異常麻煩。比如我對一個表單進行了反覆修改,生成了N份歷史修改數據,獲取 最新數據 時需要對這些歷史數據進行合併,變得異常麻煩。這次業務能夠使用在於,
-
拆分寫,能夠有效的減少數據傳輸。
-
讀寫可以分離,分別擴展
-
通過事件溯源,可以恢復數據到任意編輯的版本
具體設計
系統整體採用CQRS+Event-Sourcing來實現
CQRS
CQRS模式通過使用不同的接口來分離讀取數據和更新數據的操作。CQRS模式可以最大化性能,擴展性以及安全性, 還會爲系統的持續演化提供更多的彈性,防止Update命令在域模型Level發生衝突。
文本編輯這塊領域模型很薄,沒有什麼領域校驗與約束,按讀取數據/更新數據分離,當讀寫壓力不同時,以後可以拆分成不同的服務,分別擴展。
Event Sourcing(事件溯源)
a.不保存對象的最新狀態,而是保存對象產生的所有事件 b.通過事件溯源(Event Sourcing,ES)得到對象最新狀態;
系統整體分爲三大部分
一. command
所有數據修改命令,更新Command、撤銷Command、覆蓋Command 會持久化存儲到CommitRepository中。然後發出事件消息
二. event-handle
對於文本編輯這個case,事件處理主要是合併提交的command event。否則事件溯源時,需要處理的數據更新事件太多,耗時太長。
三. query
查詢數據,能夠根據修改記錄獲取任意commit的數據。
三大部分分離,可以部署爲單個服務,也可以解耦爲多個服務,便於擴展。
需要解決的問題
-
如何保證事件的有序性
CQRS的一個典型問題就是生產端的事件順序和消費端的事件順序不一致,導致數據不一致的問題。如何去解決呢?
Command處理部分處理所有的數據更新部分,會生成一個全局有序的commitid,代表着更新的順序。也就是生產端的事件順序,但是到達我們消費端的順序卻不一定是這個順序。所以消費端,事件處理完成後,會更新消費的最新commitid。如果當前事件的commitid小於最新的commitid,事件遺棄。
-
如何保證讀數據性能 event handle部分會去合併commit,所以讀數據不是從所有的修改數據commit中合併數據。數據已經預先處理了,所以會大大加快讀取效率,可以控制待合併的數據在5~10commits範圍之內。
-
數據會丟失嗎
系統分離後,沒有事務保證,數據的完整性如何保證。
當數據更新Command寫入成功後,代表這條數據更新成功,這個數據就不會丟失。因爲這些數據都已經被持久化了,剩下的問題就是讀取這些提交的Command Commit。我們可以通過合併這些commit,得到最新的完整數據。所以即使event-handle部分宕機了,仍然可以讀取到最新的數據。
說明
這個案例還是沒有應用框架,調研過axon,評估目前還不是太適合用,代碼可讀性不強,帶來的好處不明顯。後續再考慮是否需要引入框架。