ddd模型的pom版本怎么管理_DDD 中的那些模式 — CQRS
DDD 作為一種系統分析的方法論,最大的問題是如何在項目中實踐。而在實踐過程中必然會面臨許多的問題,「模式」是系統架構領域中一種常見的手段,能夠幫助開發人員與架構師在遭遇某種較為棘手,或是陌生的問題時,參考已有的成熟經驗與解決方案,從而優雅的解決自己項目中的問題。
從本期開始,我會開始介紹 DDD 中一些常見的模式,包括這些模式的背景,作用,優缺點,以及在使用過程中需要注意的地方。而本次的主角就是 CQRS,中文名為命令查詢職責分離。
為何要使用?
毋庸置疑「領域」在 DDD 中占據了核心的地位,DDD 通過領域對象之間的交互實現業務邏輯與流程,并通過分層的方式將業務邏輯剝離出來,單獨進行維護,從而控制業務本身的復雜度。
但是作為一個業務系統,「查詢」的相關功能也是不可或缺的。在實現各式各樣的查詢功能時,往往會發現很難用領域模型來實現。假設在用戶需要一個訂單相關信息的查詢功能,展現的是查詢結果的列表。列表中的數據來自于「訂單」,「商品」,「品類」,「送貨地址」等多個領域對象中的某幾個字段。這樣的場景如果還是通過領域對象來封裝就顯的很麻煩,其次與領域知識也沒有太緊密的關系。
此時 CQRS 作為一種模式可以很好的解決以上的問題,那么具體什么是 CQRS 呢?又如何實現呢?
什么是 CQRS?
CQRS — Command Query Responsibility Segregation,故名思義是將 command 與 query 分離的一種模式。query 很好理解,就是我們之前提到的「查詢」,那么 command 即命令又是什么呢?
CQRS 將系統中的操作分為兩類,即「命令」(Command) 與「查詢」(Query)。命令則是對會引起數據發生變化操作的總稱,即我們常說的新增,更新,刪除這些操作,都是命令。而查詢則和字面意思一樣,即不會對數據產生變化的操作,只是按照某些條件查找數據。
CQRS 的核心思想是將這兩類不同的操作進行分離,然后在兩個獨立的「服務」中實現。這里的「服務」一般是指兩個獨立部署的應用。在某些特殊情況下,也可以部署在同一個應用內的不同接口上。
Command 與 Query 對應的數據源也應該是互相獨立的,即更新操作在一個數據源,而查詢操作在另一個數據源上。看到這里,你可能想到一個問題,既然數據源進行了分離,如何做到數據之間的同步呢?讓我們接著往下看。
實現 CQRS
讓我們先看一下 CQRS 的架構圖:
從圖上可以看到,當 command 系統完成數據更新的操作后,會通過「領域事件」的方式通知 query 系統。query 系統在接受到事件之后更新自己的數據源。所有的查詢操作都通過 query 系統暴露的接口完成。
從架構圖上來看,CQRS 的實現似乎并不難,許多開發者覺得無非是「增刪改」一套系統一個數據庫,「查詢」一個系統一個數據庫而已,有點類似「讀寫分離」,并沒有什么特別的地方。但是真正要使用 CQRS 是有許多問題與細節要解決的。
CQRS 帶來的問題
事務
其實仔細的思考一下,你應該很快會發現 CQRS 需要面臨的一個最大的問題: 事務。在原本單一進程,單一數據源的系統中,依靠關系型數據庫的事務特性能夠很好的保證數據的完整性。但是在 CQRS 中這一切都發生了變化。
當 command 端完成數據更新后,需要通過事件的形式通知 query 端系統,這就存在著一定的時間差,如果你的業務對于數據完整的實時性非常高,那么可能 CQRS 不一定適合你。
其次一個 command 觸發的事件在 query 端可能需要更新數個數據模型,而這也是有可能失敗的。一旦更新失敗那么數據就會長時間的處于不一致狀態,需要外部的介入。這也是在使用 CQRS 之前就需要考慮的。
從事務的角度來看 CQRS,你需要面對的是問題從根本來說是個最終一致性的問題,所以如果你的團隊在這塊沒有太多經驗的話,那么需要提前學習并積累一定的經驗。
基礎設施與技術能力的挑戰
CQRS的另一個問題是沒有一個成熟易用的框架,Axon 可能算一個,但是 Axon 本身是一個重量級且依賴性較高的框架。為了 CQRS 而引入 Axon 有點舍本逐末的意思,因此大部分時間你不得不自己動手實現 CQRS。
一個成熟可靠的 CQRS 系統對于基礎設施有一定的要求,例如為了實現領域事件,一個可靠的消息中間件是不可或缺的。不然頻繁丟失事件造成數據不一致的情況會讓運維人員焦頭爛額。之前提到的分布式事務與最終一致性的問題也需要專門的中間件或是框架的支持,這些不僅僅提升了對基礎設施的要求,對于開發,運維也提出了更高的要求。
開發過程中需要加入對于事件的支持,系統設計的思路也同樣需要一定的轉變。在定義 command 時需要設計對應的事件,設計事件的類型與數據結構,所以在這方面也對開發團隊提出了新的要求。
因此在開始使用 CQRS 之前不妨對自己團隊的基礎設施以及開發能力做一次全面的評估,盡早的識別出短板,并進行有目的的改進與強化,避免在開發過程中別某些問題卡住。
查詢模型的設計
雖然 CQRS 為我們分離了領域模型和服務于查詢功能的數據模型,但這意味著我們需要設計另一套針對查詢功能的數據模型。一般比較簡單的做法是按照查詢功能所需的數據進行設計,即針對每一個查詢接口設計一個數據視圖,當收到領域事件時更新有關聯的數據視圖。
但是這種簡單做法帶來的問題就是當查詢接口越來越多時就會難以管理,仍然需要按照 DDD 中劃分 BC 的思路將屬于一個 BC 的查詢集中管理作為整個查詢系統的一個上下文,或是干脆獨立出來做一個微服務。所以即使引入了 CQRS,我們依然需要使用領域驅動的思路設計查詢接口。
與 Event Sourcing 的關系
Event Sourcing是由 Martin Fowler 提出的一個企業架構模式。簡單的來說它會將系統所有產生業務行為以 append-only 的形式存儲起來,通俗的說就是「流水賬」。它的優點是可以「回溯」,因為記錄了每一次數據變動的信息,所以當出現 bug 或是需要排查業務數據問題時就非常的方便。但是它的缺點同樣明顯,就是當需要查詢最新狀態的數據時需要做大量的計算,例如賬戶余額這樣的數據。
許多討論 CQRS 的文章中都會談及 Event Sourcing,認為這是兩個需要配套使用的模式。但是從我實際使用的角度而言,這兩個模式其實并沒有什么必然的聯系。Command 端只需要關心領域模型的更新成功與否,同時使用 Aggregate 這樣的領域對象保證數據的完整性,而 Query 端關心的是接收到領域事件后更新對應的數據模型,對于「回溯」這樣的特性并沒有強制的要求。的確 Event Sourcing 可以幫助我們構建更為穩定,功能更為強大的 CQRS 系統,但是 Event Sourcing 本身的復雜性可能比 CQRS 有過之而無不及,所以在沒有特殊需要的情況下,CQRS 與 Event Sourcing 不需要綁在一起。
不同類型的數據存儲引擎
這一點其實不能算是問題,更多的是一項挑戰或是優勢。由于分離了領域模型與數據模型,因此意味著我們可以在 Query 端使用與查詢需求更為貼近的數據存儲引擎,例如 NoSQL,ElasticSearch 等。
比較常見的情況是 Command 端依然使用傳統的關系型數據庫,但是對于那些比較特殊的查詢則使用專門的數據存儲。例如在一些基于關鍵字進行全文檢索的場景,如果依然使用關系型數據庫,通過 like 這樣的 SQL 查詢,很容易遇到性能問題。此時則可以將數據存儲換為 ElasticSearch 這樣的檢索引擎,通過反向索引提取關鍵字查詢,在性能方面會得到非常明顯的提升。在另一些需要非結構化數據查詢的場景,Json 是一種不錯的存儲格式,雖然現在比較新版本的關系型數據庫都提供了 Json 格式的存儲與查詢,但是 MongoDB 這樣的文檔型數據庫顯得更為簡單高效,此時 Query 端靈活的優勢就更為明顯。
小結
CQRS 在 DDD 中是一種常常被提及的模式,它的用途在于將領域模型與查詢功能進行分離,讓一些復雜的查詢擺脫領域模型的限制,以更為簡單的 DTO 形式展現查詢結果。同時分離了不同的數據存儲結構,讓開發者按照查詢的功能與要求更加自由的選擇數據存儲引擎。
同樣的,CQRS 在帶來架構自由與便利的同時也不可避免的引入了額外的復雜性與技能要求,例如對于分布式事務,消息中間件的管理,數據模型的設計等等,所以在引入 CQRS 之前需要對團隊能力與現有架構做仔細的分析,對短板進行必要的提升。如果現有系統邏輯較為簡單,只是一些 CRUD,那么并不建議使用 CQRS。但是如果你的業務系統已經非常龐大,業務流程龐雜,邏輯繁瑣,那么不妨嘗試使用 CQRS 將 Command 與 Query 進行拆分,將領域模型與數據模型的邊界劃分的更清晰些。
歡迎關注我的微信號「且把金針度與人」,獲取更多高質量文章
總結
以上是生活随笔為你收集整理的ddd模型的pom版本怎么管理_DDD 中的那些模式 — CQRS的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker脚本安装 阿里云_让运行在
- 下一篇: 无法读取配置节aspnetcore_传奇