Go architectures · 3 MIN READ
CQRS: different models for commands and queries
Separate change rules from read shape without assuming two databases.
Separate change rules from read shape without assuming two databases. CQRS separates the model that changes state from the model that answers questions. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.
Architecture · A MAP OF THE IDEA
- Command validates a change
- Authoritative state changes
- Query returns a read model
Useful when
- Writes have rules and reads need different shapes
- Read scaling has a measured need
Watch out
- Async projections introduce lag and rebuild work
- Simple CRUD may gain only extra types
Implementation & operation cost: Medium to high
Fit depends on the task
Editorial suitability, not benchmarks or a universal ranking. Styles describe different axes and can be combined.
The design decision
CQRS separates the model that changes state from the model that answers questions. Commands can protect invariants while queries return compact summaries. Start in one process and one database; asynchronous projections and event sourcing are optional additional decisions.
A complete Go example
Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.
package main
import (
"errors"
"fmt"
)
type Ledger struct{ balance int }
func (l *Ledger) Deposit(n int) error {
if n <= 0 {
return errors.New("positive deposit required")
}
l.balance += n
return nil
}
type Summary struct{ Available int }
func (l Ledger) Summary() Summary { return Summary{Available: l.balance} }
func main() {
l := &Ledger{}
_ = l.Deposit(50)
fmt.Println(l.Summary().Available)
}
// Output:
// 50
Deposit is the command boundary; Summary is the query shape. Both read the same in-memory state here, so there is no projection lag. Separating methods does not by itself require queues, extra services or event replay.
Tradeoffs and the simpler alternative
If a projection becomes asynchronous, explain stale reads and rebuild procedures to clients and operators. Ordinary CRUD may not need separate models. Add CQRS where read shapes and write rules diverge, rather than to make simple storage appear architectural.
Separate models before separating infrastructure
Commands express intent and validate invariants; queries return a shape optimized for a reader. Both can initially use the same process and database. A dashboard projection may join fields that never belong in the command aggregate. CQRS does not require event sourcing, a broker or separate databases. Keep authorization and sensitive-field filtering on the query path too: a read model is not automatically safe because it does not mutate state.
Design projection lag as user-visible behavior
With an asynchronous projection, a successful command can be durable before the query model catches up. Return a command identifier or version and decide whether the UI waits for that version, shows pending state or reads a command result directly. Apply projection events idempotently and in the required order. Record a durable cursor; an in-memory last-seen marker cannot support restart. Deleted or corrected records need explicit projection behavior.
Rebuild and test a projection
A projection test should apply duplicate input and verify it does not double-count. Test out-of-order events according to the chosen policy, a restart from the saved cursor and schema changes. Rebuild a new projection beside the old one, compare counts and representative queries, then switch readers. Separate read/write models when their needs differ enough to pay for synchronization. A small application with one ordinary CRUD shape can keep one model.
TAKE THIS WITH YOU
Separates the write model from the read model. If a projection becomes asynchronous, explain stale reads and rebuild procedures to clients and operators. Ordinary CRUD may not need separate models. Add CQRS where read shapes and write rules diverge, rather than to make simple storage appear architectural.
Check your understanding
Which statement best describes CQRS: different models for commands and queries?
Go to the source
Progress stays in this browser. No account needed.