SuperAPI AI Directory

cqrs

General & other

View repository

Updated 10-15 20:31

About this skill

CQRS

Command ≠ query. Two models when one relational model already lies to one of the sides. It is not two microservices nor mandatory Event Sourcing.

Domain: ddd. Command persistence: aggregate. Integration: event-driven-architecture. Decision: software-architecture.

When (not) to use

Use when: the read screen does not fit the aggregate; writes with heavy invariant; denormalized read store (search, dashboard).

Do not use when: symmetric CRUD. “Every MediatR handler” without two models is theater.

Command vs query

Command Query
Intent Change state Answer question
Model Aggregate / write store DTO / view / read store
Idempotency Explicit in critical writes No effect
Failure 4xx from invariant empty 404
  • Commands: PlaceOrder. They do not return the screen graph — they accept 202 + id or the minimum state.
  • Queries: do not go through the aggregate just to “reuse”. GetOrderDetails reads the projection or read SQL.
Client → PlaceOrder (write DB)
      → OrderPlaced → projector → read DB / cache
Client → GetOrderHistory (read)

One database vs two

  • Same DB, write tables + read views/tables: lightweight CQRS. Local transaction possible.
  • Two stores (Postgres + Elasticsearch): eventual consistency. UI handles delay. Outbox for projecting.

Do not start with two databases. Measure the query.

Synchronize the read model

  • After the write commit: in-process projector, or event + consumer (event-driven-architecture).
  • Projection idempotent (upsert by order_id + version).
  • Version/event on write prevents old projections from overwriting new ones.

Event sourcing (optional)

The write model is the event stream. Aggregate rebuild = fold.

Only if history is a requirement (legal audit, time-travel, truth = facts). Cost: upcasting, snapshots, mandatory projections, team learning curve.

Do not do ES “to have CQRS”. CQRS lives without ES.

Application

  • Command bus (MediatR, etc.) is not CQRS. It is a dispatcher. CQRS = distinct models.
  • Authz on the command (can mutate?) and on the query (can view?) — authentication-authorization.
  • Clean: port of OrderRepository (write) ≠ OrderDetailsQuery (read) — clean-architecture.

Anti-patterns

  • Command that returns the entire page “to avoid doing a GET”
  • Query that calls three services and persists
  • Event sourcing in user registration
  • Two microservices “command-service” and “query-service” on day 1
  • Non-idempotent projection

Integration with other skills

  • Aggregates: ddd
  • Events/outbox: event-driven-architecture
  • Modules: modular-monolith
  • Services: microservices-architecture
  • HTTP: api-design (POST command, GET query)

Completion criteria

  • Commands and queries with justified models
  • Read model updated idempotently if separate
  • No ES unless history is the product
  • Authz on both sides
  • UI aware of eventual delay if there are two stores