CQRS: making application boundaries explicit

CQRS separates operations that change application state from operations that answer questions about it. This makes the difference between changing state and reading it explicit; it does not require separate services, databases, or an event bus.

For a simple state change, a conventional update may be enough. A clearer command boundary can help when a generic update hides the action, its rules, a permission check, or a consequence the application must handle. Queries can be shaped around what callers need without changing application state.

The read and write paths can remain separate within one application and one database. A separate read model or store can help when the shape or load of queries differs, but it also brings synchronization, stale data, recovery work, and another operational responsibility. Use that extra boundary only when its benefits justify the cost.

  1. When CRUD stops describing the work

    CRUD is a good fit for simple data changes such as a display name or team setting. It becomes vague when an edited DTO hides the action, rules, permissions, and consequences behind changed fields. The post traces the move from generic updates to CQS and CQRS. The point is not replacing every update, but naming business actions when the application needs to understand them.

  2. CQS and commands: make the application understand intent

    A changed DTO can show a new state without naming the action a user asked the application to perform. A command such as `DeactivateInventoryItem` makes the action and required reason visible. That gives validation, authorization, state rules, and later consequences one place to live. CQS is a small API rule, not a requirement for separate services, databases, or an event bus.

  3. Minimal CQRS: separate code paths, keep one application and one database

    Minimal CQRS keeps commands and queries in one deployed application using one database. The paths differ because commands protect state changes while queries return data shaped for a caller. This retains transactions and immediate reads after a command. It does not provide independent read scaling, denormalized reporting projections, or isolation from a busy write workload.

  4. CQRS with a separate read store: useful projections, new guarantees

    A separate read store lets a query model match its screen or report instead of the write model. It can remove expensive joins and let read traffic scale independently. The new boundary also creates a synchronization problem. Reads can be stale, projections must recover safely, and the system has to state what a user should expect after a command.