Skip to main content

CQRS — Command Query Responsibility Segregation

Separate write models (commands) from read models (queries) when their shape, load, or consistency needs differ.

When to use

  • Read-heavy with complex projections (denormalized views)
  • Read and write have very different scaling needs
  • Natural fit with Event Sourcing

Tradeoffs

  • Two models to maintain + eventual consistency between write and read side
  • Overkill for simple CRUD
type PlaceOrderCmd struct { CustomerID string; Total float64 }
type OrderSummaryQuery struct { CustomerID string }

type Handler interface{ Handle(msg any) (any, error) }

func dispatch(h map[string]Handler, msg any) (any, error) {
key := fmt.Sprintf("%T", msg)
if handler, ok := h[key]; ok {
return handler.Handle(msg)
}
return nil, fmt.Errorf("no handler for %s", key)
}

Gotcha: CQRS ≠ Event Sourcing. You can have one without the other.