Pattern · Draft

CQRS

Separate the write model from the read model so each can be shaped for its own job.

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