Command Query Responsibility Segregation (CQRS)

Java Updated Sep 19, 2026

Scope — splitting a system’s write model from its read model: what CQRS buys (simplicity, scalability, speed), what it costs (eventual consistency, projectors, duplication), and when it is worth it. See also: ../backend/api_design.md — the API surface in front of it; ../backend/be_programming_notes_pt2.md — outbox, sagas and idempotent consumers, which a CQRS projector needs.

Command Query
Changes state Yes No
Returns data No (an id/ack at most) Yes
Model (typical, not required) Normalized, strongly consistent, invariant-enforcing De-normalized, read-optimised (materialized views)
Scales with Write throughput Read throughput — usually the far larger number

CQRS is not event sourcing; the two are often used together (events as the write log, projections as the read model) but either works without the other.

What CQRS actually requires is only the split — separate models (or even just separate code paths) for commands and queries. The row above is what teams usually reach for, not part of the contract: you can do CQRS against one normalized database with no projector and no eventual consistency at all (see the middle ground in When to use it).

  • 命令與查詢責任分離 (command, query)

  • 分開設計以下兩種操作:會改變系統狀態但不會回傳值的操作,稱之為Command, 以及不會改變狀態但會回傳值的操作,稱為Query

  • Pros : 3S

  • Simplicity: CQRS將系統分成Write Model與Read Model,這兩種模型的行為與責任大不相同。從單一責任原則(Single Responsibility Principle;SRP)的角度來看,CQRS進一步簡化不同模型之內的複雜度。 寫入模型: Strong consistency normalized data model one-way dependency 讀取模型: Eventually consistency de-normalized data model (materialized view) any-way dependency

  • Scalability: 很多系統的讀取頻率遠大於寫入,例如在電子商務系統中大部分的使用者都在瀏覽資料,少部分的操作才會改變系統狀態。在這種情況下,套用CQRS可以單獨針對讀取部分加以拓展,如圖3所示。

  • Speed (Performance): 綜合上述兩個優點,CQRS可以提升系統反應速度與效能,因為開發人員可以分別針對寫入端與讀取端採取不同的優化策略。例如,寫入端採取Event Sourcing簡化與加速寫入操作,讀取端因為不會改變狀態,可以用各種快取工具加快讀取。圖4是ezKanban團隊成員杜奕萱在她的碩士論文《套用命令與查詢責任分離以簡化聚合依賴:以 ezKanban 為例》中針對套用CQRS之後ezKanban的GetBoardContent查詢所做的效能測試,可以發現套用CQRS之後有著非常巨大的讀取效能提升。

  • CQRS provides us a convenient way to select separate domain models appropriate for write and read operations; we don’t have to create a complex domain model supporting both

  • It helps us to select repositories that are individually suited for handling the complexities of the read and write operations, like high throughput for writing and low latency for reading

  • It naturally complements event-based programming models in a distributed architecture by providing a separation of concerns as well as simpler domain models

  • Cons

    • implementation complexity

      • example :
      • 例如,如圖1所示,ezKanban在資料庫端也套用CQRS,因此讀取資料庫與寫入資料庫之間的狀態同步是最終一致性。為了維持最終一致性,就會有新的設計工作產生:同步的訊息就需要考慮順序(ordering)與at least once等議題,而負責產生讀取資料庫的Projector(投影器)設計則須考慮idempotent與replay events等問題。另外,如何撰寫Projector也是一個問題。
    • Only a complex domain model can benefit from the added complexity of this pattern; a simple domain model can be managed without all this

    • Naturally leads to code duplication to some extent, which is an acceptable evil compared to the gain it leads us to; however, individual judgment is advised

    • Separate repositories lead to problems of consistency, and it’s difficult to keep the write and read repositories in perfect sync always; we often have to settle for eventual consistency

When to use it

Reach for CQRS when Don’t when
Reads vastly outnumber writes, and the two need different storage or shapes The domain is simple CRUD — you are adding a second model for nothing
The write model is genuinely complex (invariants, aggregates) while reads are flat projections Readers cannot tolerate any staleness
Read and write need to scale, deploy or fail independently The team has no appetite for eventual consistency and its debugging

A middle ground that costs almost nothing: keep one database, but separate the code paths — command handlers that mutate aggregates, and query handlers that read projections/DTOs directly. Split the storage only when the traffic asks for it.


References