CQRS
叫命令查詢職責分離,事實上就是讀寫分離的意思。
不過這裏的讀寫分離和我們通常所理解的資料庫級別的讀寫分離是兩個不同的概念。
CQRS指的讀寫分離是指在應用程式內部的程式碼級別的讀寫分離。
在本文中,我將對此做出詳細解釋。
CQS思想
CQS
:命令和查詢分離:Command and Query Segregation
。其核心思想是在任何一個物件的方法可以劃分爲兩類:
查詢:獲取資料,返回查詢資料,但不改變資料狀態。
命令:改變資料狀態,不返回任何資料。
CQRS模式的核心設計理念來自於一條設計原則,即單一職責原則。
所謂單一職責原則,指的是一個技術元件只應該負責具體一項職責。
而不應該有多個導致該元件發生狀態變化的操作。
基於CQS
的思想,任何一個方法都可以拆分為命令和查詢兩部分,如下:
private int data = 0; private int update(int value) { data += value; return data; }
上述方法既改變了資料,又返回了資料狀態。
如果按照
CQS
的思想,則該方法可以拆成Command
和Query
兩部分,如下:
private void update(int value) { data += value; } private int query() { return data; }
對於命令側是否返回資料實際業務訴求中並不一定能夠完全統一。
比如:
某些業務場景下可能會有返回業務主鍵的訴求,比如下單操作返回訂單號。
基本原則
CQS的主要原則是:
一個方法要麼是命令,要麼是查詢,但不能兩者兼有。
這種分離有助於提高程式碼的可讀性和維護性,因為它明確了方法的用途。
CQRS架構
Command and Query Responsibility Segregation
即命令查詢職責分離,是一種將命令和查詢的責任明確分離的架構模式。
這種模式進一步擴充套件了CQS的思想,適用於更大規模的系統架構。
架構思想:
CQRS將系統的讀操作和寫操作分離到不同的模型中:
命令模型(
Command Model
):
處理資料的寫操作(建立、更新、刪除)。
查詢模型(
Query Model
):
處理資料的讀操作(查詢)。
這種分離可以透過不同的資料模型、資料庫甚至服務來實現,從而最佳化讀寫效能和可伸縮性。
CQRS
模式的應用非常簡單,如下圖所示:
假設服務為
UserService
,在非CQRS
模式下同時包含了查詢和更新服務介面。
public class UserService { // 根據id查詢使用者 UserId getUserId(int userId); // 更新使用者 void updateUser(User user); }
應用
CQRS
模式之後的UserService
被拆分成了兩個介面,分別承擔查詢和寫職責。
/** 命令服務 */ public class UserCommandService { void updateUser(UserCommand command); } /** 查詢服務 */ public class UserQueryService{ User getUserById(int userId); }
最終一致性
採用
CQRS
後,查詢和命令兩側通常會採用獨立的資料模型。
採用CQRS模式並沒有強制要求必須要進行資料模型的分離。
在這種架構模式下,命令側的資料變化後及時同步(事件、訊息佇列)到查詢側,兩側資料並非實時。
在一定的延時後兩側資料最終達成一致。
最後總結
CQRS
的使用者可以根據實際情況,將讀寫分離開單獨部署,然後引入領域事件,使用訊息佇列做通訊。
但是這些都是基於不同業務場景的架構選擇,而非
CQRS
本身的要求。實際上
CQRS
只是一種非常簡單的模式而已,並沒有和事件、訊息佇列這些有強關聯。讀寫分離部署+訊息通訊:
會帶來額外的系統複雜性和更高的運維成本。
《重構:改善既有程式碼的設計》的作者也提醒要小心使用
CQRS
,不推薦將CQRS
複雜化處理。
參考資料:
CQRS 模式
在微服務中應用簡化後的 CQRS 和 DDD 模式