摘要

在之前的文章DDD-CQRS能解什麼問題中,闡述了什麼是CQRS。但是並沒有業務需求可以應用CQRS。最近需要處理一個文本增量更新的業務,經過需求分析後,嘗試使用CQRS來解這個問題

問題分析

一個文本頁面編輯,對象很大,之前是全量保存。涉及到的網絡傳輸對象比較大,經常超時OOM,所以交互改成,只保存修改的部分,也就是增量更新。

之前業務中沒法使用CQRS,在於使用CQRS後,數據的維護變得異常麻煩。比如我對一個表單進行了反覆修改,生成了N份歷史修改數據,獲取 最新數據 時需要對這些歷史數據進行合併,變得異常麻煩。這次業務能夠使用在於,

  1. 拆分寫,能夠有效的減少數據傳輸。

  2. 讀寫可以分離,分別擴展

  3. 通過事件溯源,可以恢復數據到任意編輯的版本

具體設計

系統整體採用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的數據。

三大部分分離,可以部署爲單個服務,也可以解耦爲多個服務,便於擴展。

需要解決的問題

  1. 如何保證事件的有序性

CQRS的一個典型問題就是生產端的事件順序和消費端的事件順序不一致,導致數據不一致的問題。如何去解決呢?

Command處理部分處理所有的數據更新部分,會生成一個全局有序的commitid,代表着更新的順序。也就是生產端的事件順序,但是到達我們消費端的順序卻不一定是這個順序。所以消費端,事件處理完成後,會更新消費的最新commitid。如果當前事件的commitid小於最新的commitid,事件遺棄。

  1. 如何保證讀數據性能 event handle部分會去合併commit,所以讀數據不是從所有的修改數據commit中合併數據。數據已經預先處理了,所以會大大加快讀取效率,可以控制待合併的數據在5~10commits範圍之內。

  2. 數據會丟失嗎

系統分離後,沒有事務保證,數據的完整性如何保證。

當數據更新Command寫入成功後,代表這條數據更新成功,這個數據就不會丟失。因爲這些數據都已經被持久化了,剩下的問題就是讀取這些提交的Command Commit。我們可以通過合併這些commit,得到最新的完整數據。所以即使event-handle部分宕機了,仍然可以讀取到最新的數據。

說明

這個案例還是沒有應用框架,調研過axon,評估目前還不是太適合用,代碼可讀性不強,帶來的好處不明顯。後續再考慮是否需要引入框架。

DDD系列

我們團隊是如何落地DDD的(1)

可落地的DDD的(2)-爲什麼說MVC工程架構已經過時

可落地的DDD(3)-如何利用DDD進行微服務的劃分

可落地的DDD(4)-如何利用DDD進行微服務的劃分(2)

可落地的DDD(5)-戰術設計

如何避免寫出爛的業務代碼(1)-領域對象與領域服務

如何避免寫出爛的業務代碼(2) DDD整改

DDD-CQRS能解什麼問題

一次關於聚合根的激烈討論

相關文章