摘要
在之前的文章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,評估目前還不是太適合用,代碼可讀性不強,帶來的好處不明顯。后續再考慮是否需要引入框架。