Sign inGet started
Skills / Software-architecture

CQRS

CQRS is simply the creation of two objects where there was previously only one.

CQRS (Command Query Responsibility Segregation) is an architectural pattern formalized by Greg Young that splits a system's write model (Commands) from its read model (Queries) into independently optimized halves. Paired with Event Sourcing, it enables full audit trails, temporal querying, and purpose-built projections — without the compromises of a single CRUD model trying to do everything. It is a pattern for specific bounded contexts where complexity warrants it, not a top-level architecture for every service.

By Greg Young · Free
Specimen 01 · Live diagnosisCQRS
Input

“We're building an order management system. Right now we have a single Order table and REST endpoints like PUT /orders/:id that update whatever fields…”

Diagnosis
Let's design your CQRS implementation.
Full transcript ↓
Calibrated referenceagent-skills.ai
The gap

Separate writes from reads — design systems where commands and queries never compete

Greg Young's CQRS framework splits every domain operation into one of two types: a Command (an expression of intent that changes state and returns nothing) or a Query (a data retrieval that has no side effects). On the write side, Commands are processed by Aggregate Roots — the consistency boundary — which validate, apply domain logic, and emit Domain Events. Those Domain Events are persisted to an append-only Event Store rather than overwriting current state. On the read side, Projections (also called Read Models) replay or subscribe to the event stream to build denormalized, query-optimized views. Young is emphatic that CQRS belongs inside specific Bounded Contexts, not applied wholesale to an entire system, and that the pattern is only warranted when the complexity of separate read/write optimization justifies the trade-offs of eventual consistency.

The problem

CRUD architectures force a single model to serve both writes and reads — compromising both. The write model bloats with query-shaped data; the read model is contorted by update constraints. As systems grow, this produces slow reads, complex transactions, no audit trail, and no clean integration surface for other services. Most developers default to CRUD everywhere because they lack a decision framework for when a different model is warranted — and when it isn't.

The solution

Walk away with a working CQRS design for your specific domain: named commands, aggregate boundaries, domain events with proper contracts, and projections that serve your actual query needs — plus a clear-eyed assessment of whether you should be using this pattern at all.

You bring
  • A description of the domain or use case you're modeling (e.g., order management, account ledger)
  • Existing data model, CRUD endpoints, or system sketch you want to refactor
  • Questions about specific operations: which are commands, which are queries, where consistency matters
  • Target programming language or framework (C#, Java, Go, Python, etc.)
You get
  • A named set of Commands and Domain Events with defined contracts (intent → aggregate → event)
  • Aggregate root implementation with apply() methods, event replay logic, and consistency rules
  • Projection handlers that build specific Read Models from the event stream
  • A CQRS Fitness Assessment — an explicit recommendation on whether this use case warrants CQRS/ES or is over-engineering
In action

Watch the methodology work.

Three specimens from a single real session: the same situation, unaided and calibrated, the full transcript, and the skill answering live in the channel where the work happens.

Fig.01The same situation, two ways
Specimens A · B
Specimen AVague
Unaided

“Your Order service has a single `orders` table and a `PUT /orders/:id` endpoint that accepts a JSON patch. Reporting queries join this table with five others and time out under load. There's no audit trail — if a dispute arises, you can't reconstruct what happened. Adding a new report shape means modifying the core table schema. Every developer edits the same God object.”

×Vague — nothing concrete to act on.
Specimen BCalibrated
With CQRS

“Commands like `ShipOrder` and `CancelOrder` enforce business invariants before emitting `OrderShipped` / `OrderCancelled` domain events to the Event Store. The audit trail is the event log — immutable and complete. Three separate Read Model projections serve reporting, customer-facing status, and compliance queries independently, each optimized for its shape. Rebuilding a projection after a schema change is a replay — no migration scripts.”

Specific, grounded, and ends with one clear next step.
Fig.02A real session, unedited
In-app · assistant
CQRSClaude
CQRS
Let's design your CQRS implementation. Before writing any code, I need to understand the domain. Tell me: what is the core use case you're modeling, and what does the current data model or API look like? Specifically, I'm looking for operations where write complexity (business rules, consistency) is high and read complexity (many consumers, different shapes) is also high — those are the signals that CQRS is warranted.
We're building an order management system. Right now we have a single Order table and REST endpoints like PUT /orders/:id that update whatever fields the client sends. We need an audit trail, and our reporting queries are getting really slow because they're hitting the same table.
This is a good candidate — you have both the write complexity signal (an Order has lifecycle rules: you can't ship a cancelled order, can't cancel a shipped one) and the read complexity signal (reporting queries with different shapes than the write model).
First, let's kill the PUT /orders/:id pattern. In CQRS, clients express intent, not data patches. What are the actual business operations on an order? Things like: place it, confirm it, ship it, cancel it, request a return? List the meaningful state transitions your domain actually has — not CRUD verbs.
Right, we have: PlaceOrder, ConfirmOrder, ShipOrder, CancelOrder, and MarkDelivered.
Those become your Commands. Each is a named object expressing intent, targeting a single Aggregate — the Order aggregate. Now let's define what the aggregate must enforce:
- `PlaceOrder` → only valid if no order with this ID exists yet- `ConfirmOrder` → only valid if status is `Pending`- `ShipOrder` → only valid if status is `Confirmed`- `CancelOrder` → valid if status is `Pending` or `Confirmed`, not if `Shipped` or `Delivered`- `MarkDelivered` → only valid if status is `Shipped`
These are your aggregate invariants. Each successful command emits a Domain Event in past tense: `OrderPlaced`, `OrderConfirmed`, `OrderShipped`, `OrderCancelled`, `OrderDelivered`. The aggregate stores only these events — not current state. State is reconstituted by replaying them.
Shall I write the aggregate class with `apply()` handlers for each event?
Yes — we're using Python. And what about the slow reporting queries?
Here's the Order aggregate in Python:
```pythonfrom dataclasses import dataclass, fieldfrom typing import Listfrom enum import Enum
class OrderStatus(Enum): PENDING = "pending" CONFIRMED = "confirmed" SHIPPED = "shipped" CANCELLED = "cancelled" DELIVERED = "delivered"
class Order: def __init__(self): self.id = None self.status = None self._uncommitted_events: List = []
# --- Command handlers --- def place(self, order_id, customer_id, items): if self.id is not None: raise ValueError("Order already exists") self._apply(OrderPlaced(order_id=order_id, customer_id=customer_id, items=items))
def confirm(self): if self.status != OrderStatus.PENDING: raise ValueError(f"Cannot confirm order in status {self.status}") self._apply(OrderConfirmed(order_id=self.id))
def ship(self, tracking_number): if self.status != OrderStatus.CONFIRMED: raise ValueError(f"Cannot ship order in status {self.status}") self._apply(OrderShipped(order_id=self.id, tracking_number=tracking_number))
def cancel(self, reason): if self.status in (OrderStatus.SHIPPED, OrderStatus.DELIVERED): raise ValueError(f"Cannot cancel order in status {self.status}") self._apply(OrderCancelled(order_id=self.id, reason=reason))
# --- Event apply handlers (state reconstruction) --- def _apply(self, event): self._when(event) self._uncommitted_events.append(event)
def _when(self, event): if isinstance(event, OrderPlaced): self.id = event.order_id self.status = OrderStatus.PENDING elif isinstance(event, OrderConfirmed): self.status = OrderStatus.CONFIRMED elif isinstance(event, OrderShipped): self.status = OrderStatus.SHIPPED elif isinstance(event, OrderCancelled): self.status = OrderStatus.CANCELLED elif isinstance(event, OrderDelivered): self.status = OrderStatus.DELIVERED
@classmethod def reconstitute(cls, events): order = cls() for event in events: order._when(event) return order```
For your reporting queries: build separate Read Model projections. A `OrderSummaryProjection` subscribes to the event stream and maintains a denormalized `order_summaries` table optimized for your reporting queries. When `OrderShipped` fires, it updates the shipping date column. Your reports query that table — never the event store.
Want me to build out the `OrderSummaryProjection` handler next, or define the Event Store schema?
This makes a lot of sense. One thing — should we do this for our entire system? We have a simpler product catalog service too.
No — and this is one of Young's most important points. CQRS is not a top-level architecture. For your product catalog — if it's mostly 'admin updates a product, customer reads it' with no complex business rules and no audit requirement — a simple CRUD read/write model is correct. Adding CQRS there would add operational complexity (event store, projections, eventual consistency handling) with no payoff.
Apply CQRS only where you have the signals: high write-side invariant complexity, high read-side shape diversity, audit/compliance requirements, or collaborative domain where multiple users contend on the same aggregate. Your order domain has all of these. Your product catalog likely has none of them. Scope the pattern to the bounded context that needs it.
Reply to CQRS
What the skill is doing
01Aggregate Boundary Design
02Command & Handler Modeling
03Domain Event Crafting
Fig.03The same skill, in the channel you already use
WhatsApp · live
9:41
CCQRStyping…
Messages are end-to-end encrypted. No one outside of this chat can read them.
Today
We're building an order management system. Right now we have a single Order table and REST endpoints like PUT /orders/:id that update whatever fields…9:14✓✓
Let's design your CQRS implementation.9:14
Before writing any code, I need to understand the domain.9:15
Tell me: what is the core use case you're modeling, and what does the current data model or API look like?9:15
Message

The same skill, where the work happens.

No new app to learn. The methodology runs over the WhatsApp Business API, so the answer lands as a reply in the thread you’re already in — same rigour, zero context-switch.

Reads the situation, names the pattern, returns one concrete next move.
Delivered in seconds, inside a conversation that already exists.
Specimen · WhatsApp Business API · live
Capabilities

What it does, specifically.

Each capability is a distinct move drawn straight from the source methodology — not a generic assistant guessing.

CapabilityC-01

Aggregate Boundary Design

Identify the correct Aggregate Roots for your domain — the consistency boundaries that process commands and emit events. This includes determining what belongs inside an aggregate versus outside, and where strong vs. eventual consistency is appropriate.

Based on Young's rule that an aggregate is the consistency boundary: it must enforce all invariants for commands it handles, and every command targets exactly one aggregate instance.
CapabilityC-02

Command & Handler Modeling

Design task-based Commands that express user intent rather than data mutation — replacing 'UpdateOrder' with 'ShipOrder', 'CancelOrder', 'ApproveOrder'. Each Command gets a corresponding Handler that loads the aggregate, executes domain logic, and persists resulting events.

Young insists Commands must be named in the imperative mood and encode intent, not data changes; the Handler is the only entry point to the write side and never returns domain data.
CapabilityC-03

Domain Event Crafting

Define Domain Events — the immutable, past-tense records of what happened inside an aggregate. This covers naming conventions, payload completeness, versioning strategies, and the distinction between Domain Events (internal) and Integration Events (external).

In Young's Event Sourcing model, Domain Events are the ground truth of the system: the aggregate's state is fully reconstituted by replaying its event stream, making event payload design a critical correctness concern.
CapabilityC-04

Projection & Read Model Construction

Build denormalized Read Models tailored to specific query needs by subscribing to or replaying the event stream. Each projection handles a defined set of events and maintains its own store optimized for its query shape — a product list, an account summary, a compliance audit trail.

Young's pattern decouples read models entirely from the write model: projections can be deleted and rebuilt from the event log at any time, enabling schema evolution and multiple competing views without touching command-side logic.
CapabilityC-05

CQRS Fitness Evaluation

Apply Greg Young's explicit criteria for when CQRS/Event Sourcing is and isn't appropriate. This prevents the most common CQRS mistake: applying it everywhere. The evaluation covers read/write asymmetry, audit requirements, collaboration complexity, and team capability.

Young's CQRS Documents explicitly state that CQRS is a pattern for specific bounded contexts, not a system-wide architecture, and that applying it to simple CRUD domains is harmful over-engineering — this capability encodes that decision framework.
Tested

Graded before it shipped.

Every skill is scored against independent scenarios for methodology fidelity before it goes live — not vibes, a rubric.

What it produces
OutputD-01

CQRS Bounded Context Scope Map

A diagram or structured description identifying which bounded contexts in your system warrant CQRS/ES and which should remain simple CRUD, with rationale for each decision.

OutputD-02

Command → Aggregate → Event Contract Sheet

A table or code-documented listing of each Command, the Aggregate it targets, the invariants it enforces, and the Domain Events it emits — the complete write-side contract.

OutputD-03

Projection Blueprint

A defined Read Model with its event subscriptions, handler logic, and output schema — ready to implement as a standalone projection handler in your chosen language.

OutputD-04

Event-Sourced Aggregate Implementation

Working code for an Aggregate Root: constructor, command methods, apply() event handlers, and state reconstitution from event replay — in your target language and following Young's conventions.

The source

Grounded in the original work.

Every answer traces back to a real source and the practitioner who wrote it — not a secondhand summary. Here is the source of record.

Source authorA-01

Greg Young

Greg Young is the software architect who formalized CQRS as a distinct architectural pattern, extending Bertrand Meyer's Command Query Separation principle from the method level to the service level. He is the creator of EventStore, the open-source event-sourced database, and a defining voice in Domain-Driven Design circles. He has delivered keynotes and workshops at NDC, YOW!, and QCon conferences for over a decade.

Status · Inspired by Greg Young’s work — not yet claimed. Are you Greg Young?
Primary sourceS-01

CQRS Documents

by Greg Young

Creator of EventStore; formalized CQRS pattern; DDD practitioner and conference keynote speaker at NDC, YOW!, QCon.

Read the original ↗
Citationgoodenoughsoftware.net
In the build queue

Be first to run it.

CQRS is being built right now. Leave your email and we’ll tell you the moment it goes live.

Notify meEmail
At launchI have a [domain — e.g., order management / account ledger / booking system] that currently uses CRUD. I need an audit trail and my read/write models are fighting each other. Can we design a CQRS architecture for it — starting with identifying the aggregates, commands, and domain events?