Flow
Decisions

0002 — Not event sourcing, not CQRS

State is never derived from events; one model, not separate read and write models.

Decided. State is never derived from events. There is one model, not a write model and a read model.

The accurate name for what we are doing: state-stored services with a transactional outbox. Event-driven, not event-sourced.

Why not event sourcing

The log stops being the record (see 0001), so replay no longer reconstructs state. Keeping event-sourcing machinery on top of an authoritative database gives you the costs — unbounded replay on write, snapshot management, stream-sequence concurrency control — with none of the benefit.

Why not CQRS

CQRS means a separate model for writes and reads, connected asynchronously. That buys read scaling and shape freedom at the cost of a staleness window, a second store, and a divergence question during every incident.

We do not need it. Where a wide denormalized read shape is useful, it is a view, a materialized view refreshed in the writing transaction, or a trigger-maintained table — in the same database. One source of truth, synchronously consistent, no lag.

Command/query separation at the handler level is fine and normal. That is not CQRS; it is organised code.

What we keep from the event-driven world

  • A durable, ordered, replayable event log per service (the outbox table).
  • Curated domain events as the integration contract.
  • Consumers building whatever projections they want from it.

Consequences

  • Replay is for consumers, not for us.
  • Emission completeness becomes a discipline: a state change that forgets its event row is a bug the database cannot catch. Hence the conformance check that every state change has a matching event row in the same transaction.

On this page