Pattern · Draft
CQRS
Separate the write model from the read model so each can be shaped for its own job.
Status
Draft
Implemented by
Architecture
Commands enforce domain rules and update the write model. Changes asynchronously refresh a separate read model, while queries use their own handler. The read side can scale independently and may lag behind writes.
Draft: this pattern is captured but not yet reviewed. The forces, trade-offs and failure modes below are a working position, not a finished one.
The problem
A single model asked to serve both writes and reads ends up optimized for neither: normalized for integrity, then forced to answer query-shaped reads through ad-hoc indexes and application-side joins.
The solution
Commands mutate a write model that enforces invariants; queries read from one or more projections shaped to the use case. The two sides scale, evolve, and fail independently, and each can be owned by the people who understand it.
Forces
What pulls against what.
- Clarity against complexity
- Throughput and latency against consistency
- Team and ownership boundaries
- How much eventual consistency the domain can tolerate
On AWS
- AppSync
- DynamoDB
- EventBridge
- Lambda
- DynamoDB Streams
Trade-offs
- Reads and writes each get a model suited to them, and can scale separately
- More moving parts: two models, a sync path, and the tests for both
- Reads trail writes unless you add a read-your-writes path
When not to use it
- Simple CRUD where one model is genuinely enough
- Small domains with no scale or ownership pressure
- Teams without the operational maturity to run two models
Failure modes
How it breaks when it breaks.
- The read model quietly becoming the real model, with commands bypassing it
- Unbounded projection lag under load
- Two models drifting apart with no reconciliation or contract