CQRS 與其起源#
CQRS 全名為 Command-Query Responsibility Segregation。其核心概念極為簡單:不再使用單一的統一模型,而是引入兩個模型——一個負責讀取,另一個負責寫入。
CQRS 由 Greg Young 於 2010 年提出,其理論基礎來自 Bertrand Meyer 所定義的 Command-Query Separation(CQS) 原則。
CQS 原則#
CQS 原則規定:每個方法都應該是 Command 或 Query,而非兩者兼具。換句話說,「提問不應改變答案」。
- Command:產生副作用(side-effect),回傳
void - Query:無副作用(side-effect free),回傳非
void值
遵循此原則,僅透過方法簽名就能判斷其用途,無需深入實作細節,從而提升程式碼的可讀性。
CQS 的限制#
CQS 並非在所有情況下都適用,以下兩種場景可能需要違反此原則:
- Stack.pop():同時移除元素並回傳值,拆分為兩個方法反而不合理
- 競態條件(Race Condition):當 Query 結果可能在 Command 執行前過期時,需要將兩者合併以確保操作的原子性
// 遵循 CQS
public bool fileExists(String path) {
return file.exists(path);
}
public void writeToFile(String path, String content) {
if (!fileExists(path))
throw new IllegalArgumentException();
file.writeAllText(path, content);
}
// 違反 CQS(但確保原子性)
public Result writeToFile(String path, String content) {
try {
file.writeAllText(path, content);
return Result.ok();
}
catch (fileNotFoundException e) {
return Result.fail("File not found");
}
}儘管存在例外情況,CQS 仍應作為預設的設計選擇,僅在確有必要時才偏離。
從 CQS 到 CQRS#
CQS 關注的是方法層級的分離,而 CQRS 則將相同的概念提升到模型與類別層級:
| 操作單位 | |
|---|---|
| CQS | Method-command / Method-query |
| CQRS | Command model / Query model |
CQRS 鼓勵你將單一的統一 Domain Model 拆解為兩個模型:一個處理 Command(寫入),另一個處理 Query(讀取)。

CQS 與 CQRS 的關係:CQS 作用於方法層級,CQRS 將其擴展到模型與類別層級
為什麼要使用 CQRS#
CQRS 的三大核心效益:
可擴展性(Scalability)#
在典型的企業級應用中,讀取操作遠多於寫入操作。CQRS 允許對讀寫進行非對稱擴展:
- Command 端(Create / Update / Delete):1 台伺服器
- Query 端(Read):10 台伺服器
由於 Command 與 Query 的處理本質上是不對稱的,非對稱擴展完全合理。
效能(Performance)#
即使讀寫部署在同一台伺服器上,分離後仍可針對 Query 端進行獨立優化:
- 為查詢 API 設置專屬快取
- 使用資料庫特定功能與手工優化的 SQL
- 不受 Command 端 ORM 的限制

讀寫分離架構:Write model 與 Read model 各自透過獨立 API 存取資料庫,Query 端可使用 Cache 和高度優化的 SQL
簡潔性(Simplicity)#
CQRS 最重要的效益是簡潔性。Command 端與 Query 端的需求截然不同,試圖用單一模型來滿足兩者,如同「將方形積木塞入圓形孔洞」,最終只會得到一個過度複雜且兩頭不討好的模型。
CQRS 可視為單一職責原則(SRP)在架構層級的應用:
- Commands:負責變更資料
- Queries:負責讀取資料
透過明確區分兩種不同的使用場景,引入各自獨立的模型,能大幅降低程式碼複雜度。每個模型只做一件事,且做得好。
真實世界中的 CQRS#
你可能已經在不知不覺中使用了 CQRS 模式:
- ORM 寫入 + Raw SQL 讀取:使用 Hibernate 寫入資料庫,但用 JDBC 原生 SQL 讀取——這就是 CQRS
- Database Views:為特定的讀取場景建立最佳化的資料庫視圖
- ElasticSearch:從關聯式資料庫索引資料,提供豐富的全文搜尋能力——寫入模型與讀取模型完全分離
上述場景的共通點是:寫入使用一種模型,讀取使用另一種完全獨立的模型。差別只在於讀取模型是自行建構還是借助既有軟體。
小結#
- CQRS 源自 CQS 原則,並將其擴展到架構層級
- CQRS 將統一的 Domain Model 拆分為 Command Model 與 Query Model
- 三大效益:可擴展性、效能,以及最重要的——簡潔性
- CQRS 本質上是 SRP 在架構層級的應用
- ElasticSearch、Database Views 等都是 CQRS 在真實世界中的實例