架構(gòu)設(shè)計(jì)之解析CQRS架構(gòu)模式!
在本文中,我將對(duì)此做出詳細(xì)解釋。
CQS思想
CQS:命令和查詢分離:Command and Query Segregation。
其核心思想是在任何一個(gè)對(duì)象的方法可以劃分為兩類:
- 查詢:獲取數(shù)據(jù),返回查詢數(shù)據(jù),但不改變數(shù)據(jù)狀態(tài)。
- 命令:改變數(shù)據(jù)狀態(tài),不返回任何數(shù)據(jù)。
CQRS模式的核心設(shè)計(jì)理念來(lái)自于一條設(shè)計(jì)原則,即單一職責(zé)原則。
- 所謂單一職責(zé)原則,指的是一個(gè)技術(shù)組件只應(yīng)該負(fù)責(zé)具體一項(xiàng)職責(zé)。
而不應(yīng)該有多個(gè)導(dǎo)致該組件發(fā)生狀態(tài)變化的操作。
基于CQS的思想,任何一個(gè)方法都可以拆分為命令和查詢兩部分,如下:
private int data = 0;
private int update(int value) {
data += value;
return data;
}
上述方法既改變了數(shù)據(jù),又返回了數(shù)據(jù)狀態(tài)。
如果按照CQS的思想,則該方法可以拆成Command和Query兩部分,如下:
private void update(int value) {
data += value;
}
private int query() {
return data;
}
對(duì)于命令側(cè)是否返回?cái)?shù)據(jù)實(shí)際業(yè)務(wù)訴求中并不一定能夠完全統(tǒng)一。
比如:
- 某些業(yè)務(wù)場(chǎng)景下可能會(huì)有返回業(yè)務(wù)主鍵的訴求,比如下單操作返回訂單號(hào)。
基本原則
CQS的主要原則是:
- 一個(gè)方法要么是命令,要么是查詢,但不能兩者兼有。
- 這種分離有助于提高代碼的可讀性和維護(hù)性,因?yàn)樗鞔_了方法的用途。
CQRS架構(gòu)
Command and Query Responsibility Segregation
- 即命令查詢職責(zé)分離,是一種將命令和查詢的責(zé)任明確分離的架構(gòu)模式。
- 這種模式進(jìn)一步擴(kuò)展了CQS的思想,適用于更大規(guī)模的系統(tǒng)架構(gòu)。
架構(gòu)思想:
CQRS將系統(tǒng)的讀操作和寫(xiě)操作分離到不同的模型中:
- 命令模型(Command Model):
處理數(shù)據(jù)的寫(xiě)操作(創(chuàng)建、更新、刪除)。
- 查詢模型(Query Model):
- 處理數(shù)據(jù)的讀操作(查詢)。
這種分離可以通過(guò)不同的數(shù)據(jù)模型、數(shù)據(jù)庫(kù)甚至服務(wù)來(lái)實(shí)現(xiàn),從而優(yōu)化讀寫(xiě)性能和可伸縮性。
CQRS 模式的應(yīng)用非常簡(jiǎn)單,如下圖所示:
圖片
假設(shè)服務(wù)為 UserService,在非CQRS模式下同時(shí)包含了查詢和更新服務(wù)接口。
public class UserService {
// 根據(jù)id查詢用戶
UserId getUserId(int userId);
// 更新用戶
void updateUser(User user);
}
應(yīng)用CQRS模式之后的UserService被拆分成了兩個(gè)接口,分別承擔(dān)查詢和寫(xiě)職責(zé)。
/**
命令服務(wù)
*/
public class UserCommandService {
void updateUser(UserCommand command);
}
/**
查詢服務(wù)
*/
public class UserQueryService{
User getUserById(int userId);
}
最終一致性
采用CQRS后,查詢和命令兩側(cè)通常會(huì)采用獨(dú)立的數(shù)據(jù)模型。
- 采用CQRS模式并沒(méi)有強(qiáng)制要求必須要進(jìn)行數(shù)據(jù)模型的分離。
在這種架構(gòu)模式下,命令側(cè)的數(shù)據(jù)變化后及時(shí)同步(事件、消息隊(duì)列)到查詢側(cè),兩側(cè)數(shù)據(jù)并非實(shí)時(shí)。
- 在一定的延時(shí)后兩側(cè)數(shù)據(jù)最終達(dá)成一致。
圖片
最后總結(jié)
CQRS的使用者可以根據(jù)實(shí)際情況,將讀寫(xiě)分離開(kāi)單獨(dú)部署,然后引入領(lǐng)域事件,使用消息隊(duì)列做通信。
- 但是這些都是基于不同業(yè)務(wù)場(chǎng)景的架構(gòu)選擇,而非CQRS本身的要求。
- 實(shí)際上CQRS只是一種非常簡(jiǎn)單的模式而已,并沒(méi)有和事件、消息隊(duì)列這些有強(qiáng)關(guān)聯(lián)。
讀寫(xiě)分離部署+消息通信:
- 會(huì)帶來(lái)額外的系統(tǒng)復(fù)雜性和更高的運(yùn)維成本。
《重構(gòu):改善既有代碼的設(shè)計(jì)》的作者也提醒要小心使用CQRS,不推薦將CQRS復(fù)雜化處理。