您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么寫出簡潔的CQRS代碼”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么寫出簡潔的CQRS代碼”吧!
命令和查詢責任分離(CQRS)是指將數據存儲的讀取和更新操作分開的一種模式。實施 CQRS 據稱可以提高性能、可擴展性和安全性。遷移到 CQRS 模式所創造的靈活性,使系統能夠隨著時間的推移而更好地發展。不過 CQRS 模式有一些眾所周知的陷阱,本文介紹了三種實用的場景。
CQRS 模式可以創造奇跡:它可以將可擴展性、性能、安全性最大化,甚至可以打破 CAP 定理 (1)。盡管如此,CQRS 還是因為其引入的復雜性而獲得了一個有爭議的名字。例如,Martin Fowler 在其 CQRS 文章 (2) 中認為,應該少用甚至謹慎地應用該模式。
對大多數系統來說,CQRS 增加了風險的復雜性
你應該非常謹慎地使用 CQRS
雖然 CQRS 是工具箱中的一種模式,但要注意的是,它很難用得好,如果處理不當,很容易搞壞你重要的部件。
從我的觀點來看,CQRS 帶來的復雜性在很大程度上是偶然的,也是可避免的。為了說明我的觀點,我想先討論一下 CQRS 的目標,然后分析一下基于CQRS 系統中常見的三個復雜性的來源。
CQRS 的目標
CQRS 的目標是使用多種模型來表示相同的數據,與可擴展性、可用性、安全性、性能都沒有關系。在多個模型中表示相同的數據,這就是目標,剩下的都是副產品。不信?聽聽 Greg Young 在 DDDEU2016 大會上的演講 (3) ,他說 CQRS 是為了支持 Event Sourcing (事件溯源)實現而發明的。而且大家可能都知道,Event Sourcing 模型對于寫數據來說很強大,但是對于讀數據來說卻很糟糕,這就是當年他需要 CQRS 的原因:用多個模型來表示相同的數據。
CQRS 是如何實現這個目標的?通過確保只有一個模型作為數據的源頭,所有的修改都通過這個模型來達到。
讓我們來看看這個解讀如何幫助我們解決一些復雜的問題。
復雜性陷阱一:單向命令,或者說過度的隔離
據我所知,所有的 CQRS 的定義都遵循這個模式。
CQRS 是基于 CQS 原則,它指出,操作應該被分為兩組:改變數據的命令和查詢數據的命令。一旦我們將這一原則提升到架構層面,我們就會得到一個系統,用例被隔離成相同的兩組:命令和查詢。每個用例既可以是命令,也可以是查詢,但絕對不能同時是命令和查詢。
一旦用例被隔離,我們會得到很多好處:多種模型、不同的持久化機制、獨立的可擴展性等。
你是否感覺到這里有什么問題?這個問題很微妙:所有的 CQRS 定義通常都是從解決方案 — 隔離開始,之后才定義問題 — 多模型。這就導致了對隔離太過熱衷:甚至將命令定義為單向的,操作服務器只返回 Ack/Nack 響應,必須輪詢一些讀模型存儲的實際命令才能返回結果。換句話說,復雜度如地獄式的釋放。
解決辦法:放寬隔離
讓我們退一步,重新考慮一下隔離的問題。我們已經看到,根據 CQRS 的說法,為了在多個模型中表示相同的數據,一個用例既可以寫數據,也可以讀數據。讀取模型不應該更新任何東西,這一點是不言而喻的,否則我們最終會有多個數據來源。但是,你真的應該讓你的命令空轉嗎?
其實不然,在不違反任何原則的情況下,一個命令可以安全地返回以下數據。
執行結果:成功/失敗。
如果失敗:錯誤信息或驗證錯誤。
如果成功:聚合的新版本號。
這些信息將極大地改善你的系統的用戶體驗,因為:
你不需要向外部來源查詢命令執行結果,你馬上就能得到它。在驗證命令,以及返回錯誤信息方面變得非常簡單;以及
如果你想刷新顯示的數據,你可以使用聚合的新版本來確定視圖模型是否反映了已執行的命令。不會再顯示陳舊的數據了。
說到數據,我們能不能再放寬一點隔離?在很多情況下,受影響的聚合內部包含的任何數據都可以作為命令執行結果的一部分返回。但是,這里有一點細微的差別:確保返回的數據可以在以后從其中一個讀取模型中查詢。否則,在響應沒有到達客戶端的情況下,數據可能會有潛在的丟失風險。
你可以在 Daniel Whittaker 的博客 (4) 中看到這樣一個例子,他在博客中討論了命令執行對象用于驗證命令的使用。
另外,在這個 gist 中 (5) ,你可以看到我在 C# 中使用的命令執行結果對象。
復雜性陷阱二:Event sourcing (事件溯源)
由于歷史原因,CQRS 與 Event Sourcing 模式密切相關。畢竟,CQRS 的發明就是為了讓 Event Sourcing 模式成為可能。但是,讓我們重新評估一下這兩種模式之間的耦合關系。
正如我之前所說,CQRS 的目標是允許在不同的模型中表示相同的數據。如果你正在使用 event source 域模型,你絕對需要 CQRS 來執行查詢。然而,還有很多其他合理的理由來實現 CQRS,這些理由與 event source 無關。
你的系統以不同的表示模型顯示其實體。
你必須支持不同的查詢模型(搜索、圖、文檔等)。
寫入和讀入之間的差異很大,你希望將它們獨立擴展。
你不喜歡 ORM。
這是否意味著在所有這些情況下,你必須走 Event Sourcing 路線?如果你這么做,你就深陷復雜度陷阱。Event source 是一種業務領域的建模方式,也可能是最復雜的方式。因此,只有當你的業務領域證明你的業務領域是合理的,你才應該采用 Event Sourcing。讓我們來看看如何在其他情況下實現 CQRS。
解決方案:CQRS != Event Sourcing
我們已經學會了通過編寫事件處理程序來生成投影。如果沒有事件,如何實現投影?還有一種方法可以實現投影,我稱之為“基于狀態的投影”。這個主題值得單獨寫一個帖子,但我將簡單介紹三種實現“基于狀態的投影”的方法。
1. "臟"標志
你可以通過設置 IsDirty 標志來標記一個被更新的實體,并實現一個投影引擎來查詢臟實例,并將更新的數據投影到不同的模型中。要重建投影,你只需要將所有記錄的 dirty 標志設置就可以了。
2. 追加
在關系型數據庫中,你可以在表層跟蹤提交。例如在 SQL Server 中,你有一個內置的機制,即 "rowversion" 列。這樣的功能也可以在其他關系型數據庫中實現。投射引擎將以類似于補訂的方式查詢更新的行,并將更新的數據進行投影。要從頭開始重建一個投影,必須將上次已知的提交 id “回滾” 到 0。
3. 數據庫視圖
如果你使用的是關系型數據庫,而你需要的只是用不同的模型來表示它的數據,那么數據庫視圖就很好用。沒錯,一個完全有效的 CQRS 系統可以在數據庫中實現。這可能是最不性感的解決方案 — 但它不僅可以工作,還自然地遵循了 CQRS 模式。
這些投影模型的方法可能并不酷,也不性感,但它們是有效的。我見過不少采用了這些方法的項目,它們的效果很好,沒有無端地淹沒在 event source 相關的復雜性中。
等等,我剛才是不是建議不惜一切代價忽略 Event Sourcing,因為它很復雜?當然不是! Event Sourcing 是你工具箱中最重要的工具之一。但是,作為任何工具,請在其上下文中使用它 — 能帶來商業價值的業務領域。核心子領域。另一方面,通用子域和支持子域,這些子域足夠簡單,可以用事務腳本或活動記錄模式實現,但仍然可以從 CQRS 中受益。在這種情況下,使用最簡單的工具來完成工作,并使用基于狀態的預測來獲取 CQRS 的好處。
復雜性陷阱三:好東西太多了
微服務的炒作吸引了很多人對 CQRS 的關注:如果你有一組獨立的服務需要查詢彼此的數據,那么 CQRS 就是通用的解決方案 (6) 。然而我已經看到這種方法產生了巨大的數據流圖,在服務之間投射出大量的數據。
這不一定是壞事,但在很多情況下這可能是一個信號,需要退一步重新考慮你的分解策略。有可能是你的服務過于細化,沒有反映出業務領域的邊界。如果是這種情況,你可以通過將服務邊界與相應的業務域重新對齊,大大降低架構的復雜性。
CQRS:解構
我想用 CQRS 的圖來總結一下。
此圖與您在網上可以找到的其他圖表不同。
這就是我所看到的和實現 CQRS 模式。命令有響應。定義的投射機制是抽象的,與實現細節無關。里面可能是基于事件,或者是基于狀態,甚至是數據庫視圖。最后,沒有事件源(Event Sourcing)。按照業務域的要求,對系統的業務邏輯進行建模:活動記錄、域模型或事件源域模型。
與每一個正確應用的工具一樣,CQRS 應該降低復雜性,而不是誘導復雜性。如果你的體系結構的復雜性增加了,那么你很可能做錯了。
到此,相信大家對“怎么寫出簡潔的CQRS代碼”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。