CQRS
2011 年 7 月 14 日
CQRS 代表命令查詢責任隔離。這是我第一次從 Greg Young 的描述中聽到的模式。其核心概念是,您可以使用與讀取資訊不同的模型來更新資訊。在某些情況下,這種分離可能很有價值,但請注意,對於大多數系統而言,CQRS 會增加風險複雜性。
人們用於與資訊系統互動的主流方法是將其視為 CRUD 資料儲存。我的意思是,我們有一個心智模型,其中有一些記錄結構,我們可以在其中c建立新記錄、r讀取記錄、u更新現有記錄,以及在完成後d刪除記錄。在最簡單的情況下,我們的互動都是關於儲存和擷取這些記錄。
隨著我們的需求變得更複雜,我們會逐漸遠離該模型。我們可能希望以不同的方式查看記錄儲存中的資訊,例如將多個記錄壓縮成一個記錄,或透過結合不同位置的資訊來形成虛擬記錄。在更新方面,我們可能會發現驗證規則只允許儲存某些資料組合,甚至可能會推論出與我們提供的不同的資料要儲存。

當此情況發生時,我們開始看到資訊的多重表示方式。當使用者與資訊互動時,他們會使用此資訊的各種呈現方式,而每種呈現方式都是一種不同的表示方式。開發人員通常會建立自己的概念模型,並使用該模型來操作模型的核心元素。如果您正在使用網域模型,則這通常是網域的概念表示方式。您通常也會盡可能讓持續性儲存空間接近概念模型。
這種多層表示方式的結構可能會變得相當複雜,但當人們這麼做時,他們仍然會將其解析為單一概念表示方式,而該表示方式會作為所有表示方式之間的概念整合點。
CQRS 引入的變更,是將該概念模型拆分為更新和顯示的個別模型,而它分別稱之為指令和查詢,遵循 CommandQuerySeparation 的詞彙。其原理是,對於許多問題,特別是在更複雜的網域中,讓指令和查詢擁有相同概念模型,會導致更複雜的模型,且兩者都無法發揮得很好。

我們最常將個別模型視為不同的物件模型,可能在不同的邏輯程序中執行,甚至在不同的硬體上執行。一個網路範例會看到使用者檢視使用查詢模型呈現的網頁。如果他們啟動變更,則該變更會路由到個別指令模型進行處理,而產生的變更會傳達給查詢模型,以呈現更新的狀態。
這裡有很大的變異空間。記憶體中模型可能會共用同一個資料庫,在這種情況下,資料庫會作為兩個模型之間的溝通橋樑。然而,它們也可能使用個別資料庫,有效地將查詢端的資料庫變成即時的 ReportingDatabase。在這種情況下,兩個模型或其資料庫之間需要有一些溝通機制。
這兩個模型可能不是個別物件模型,可能是相同的物件對其指令端和查詢端具有不同的介面,相當類似於關聯式資料庫中的檢視。但通常當我聽說 CQRS 時,它們明顯是個別模型。
CQRS 自然適合一些其他架構模式。
- 當我們遠離透過 CRUD 與之互動的單一表示時,我們可以輕鬆地轉移到基於任務的 UI。
- CQRS 非常適合 基於事件的程式設計模型。通常會看到 CQRS 系統分割成與 事件協作 通訊的獨立服務。這允許這些服務輕鬆利用 事件溯源。
- 擁有獨立的模型會引發有關如何讓這些模型保持一致的疑問,這會提高使用 最終一致性 的可能性。
- 對於許多網域來說,許多邏輯在更新時是必要的,因此使用 EagerReadDerivation 來簡化查詢端模型可能是合理的。
- 如果寫入模型為所有更新產生事件,您可以將讀取模型建構為 EventPosters,讓它們成為 MemoryImages,從而避免許多資料庫互動。
- CQRS 適用於複雜的網域,這種網域也受益於 領域驅動設計。
何時使用
與任何模式一樣,CQRS 在某些地方有用,但在其他地方則不然。許多系統確實符合 CRUD 心智模型,因此應該以這種風格完成。CQRS 對所有相關人員來說都是一個重大的心智飛躍,因此除非好處值得一試,否則不應嘗試。雖然我遇到過 CQRS 的成功應用,但到目前為止,我遇到的案例大多數並不那麼好,CQRS 被視為讓軟體系統陷入嚴重困境的重要力量。
特別是,CQRS 應該只用於系統的特定部分(DDD 術語中的 BoundedContext),而不是整個系統。在這種思維方式中,每個 Bounded Context 都需要自己決定如何建模。
到目前為止,我看到好處有兩個方向。首先,使用 CQRS 可能較容易處理一些複雜的網域。不過,我必須強調,這種適合 CQRS 的情況非常少數。通常,指令和查詢端之間有足夠的重疊,因此共用模型較容易。在不符合的網域上使用 CQRS 會增加複雜性,進而降低生產力並增加風險。
另一個主要好處在於處理高性能應用程式。CQRS 允許您將讀取和寫入的負載分開,讓您能獨立調整每個負載的規模。如果您的應用程式在讀取和寫入之間有很大的差異,這會非常方便。即使沒有差異,您也可以對兩端套用不同的最佳化策略。這方面的範例是對讀取和更新使用不同的資料庫存取技術。
如果您的網域不適合 CQRS,但您有會增加複雜性或效能問題的高需求查詢,請記住您仍可以使用 ReportingDatabase。CQRS 對所有查詢使用獨立的模型。有了報告資料庫,您仍可對大多數查詢使用您的主系統,但將需求較高的查詢卸載到報告資料庫。
儘管有這些好處,您在使用 CQRS 時應該非常小心。許多資訊系統很適合資訊庫的概念,資訊庫的更新方式與讀取方式相同,將 CQRS 加入這種系統可能會增加顯著的複雜性。我確實看過一些案例,即使在有能力的團隊手中,CQRS 也會大幅拖累生產力,為專案增加不必要的風險。因此,雖然 CQRS 是工具箱中不錯的模式,但請注意它很難使用得當,如果您處理不當,可能會輕易地切斷重要的部分。
延伸讀物
- Greg Young 是我聽過第一個談論這種方法的人 - 這是 他提供的摘要,我最喜歡。
- Udi Dahan 是 CQRS 的另一位倡導者,他對這項技術有 詳細的說明。
- 有一個 活躍的郵件清單,可供討論這種方法。