tadata
Back to home

CQRS & Event Sourcing: When Complexity Pays Off

#architecture#cqrs#event-sourcing#distributed-systems

Command Query Responsibility Segregation (CQRS) and Event Sourcing are often mentioned together but are independent patterns. Understanding when each adds value -- and when the overhead is not justified -- is the key architectural decision.

Traditional CRUD vs CQRS

DimensionTraditional CRUDCQRS
Data modelSingle model for reads and writesSeparate read and write models
ScalabilityScale everything togetherScale reads and writes independently
ConsistencyStrong (same DB)Eventually consistent (projection lag)
ComplexityLowHigh (two models, sync mechanism)
Query flexibilityLimited by write schemaRead models optimized per use case
Audit trailRequires additional loggingNative with event sourcing
Team structureSingle team per serviceRead/write teams can work independently
Data storage costLowerHigher (events + projections)

Event Sourcing Architecture

                        Commands
                           │
                    ┌──────▼──────┐
                    │   Command   │
                    │   Handler   │
                    └──────┬──────┘
                           │ validate + produce events
                    ┌──────▼──────┐
                    │   Event     │
                    │   Store     │  (append-only log)
                    └──────┬──────┘
                           │ publish
              ┌────────────┼────────────┐
              │            │            │
       ┌──────▼──┐  ┌─────▼────┐  ┌───▼────────┐
       │ Read    │  │ Read     │  │ External   │
       │ Model A │  │ Model B  │  │ Projector  │
       │ (list)  │  │ (search) │  │ (analytics)│
       └────┬────┘  └────┬─────┘  └────┬───────┘
            │             │             │
       ┌────▼────┐  ┌────▼─────┐  ┌────▼───────┐
       │  Query  │  │  Query   │  │  Query     │
       │  API A  │  │  API B   │  │  API C     │
       └─────────┘  └──────────┘  └────────────┘

When-to-Use Decision Matrix

CriterionScore 0 (skip)Score 1 (consider)Score 2 (strong fit)
Audit requirementsNo audit neededAudit log requiredFull temporal queries
Read/write ratioBalanced10:1 reads100:1+ reads
Domain complexitySimple CRUDModerate workflowsComplex state machines
Team size< 3 developers3-8 developers8+ developers
Query diversity1-2 query patterns3-5 query patterns6+ query patterns
RegulatoryNoneData lineage neededFull replayability required

Interpretation: 0-3 total = stick with CRUD, 4-7 = consider CQRS without event sourcing, 8-12 = CQRS + event sourcing justified.

Complexity vs Benefit Trade-off

PatternImplementation CostOperational CostDebuggingBest For
Simple CRUDLowLowEasyContent management, settings
CQRS (no ES)MediumMediumModerateHigh-read APIs, dashboards
Event Sourcing (no CQRS)Medium-HighMediumHardAudit-heavy domains
CQRS + ESHighHighVery HardFinance, regulated, temporal

Key Infrastructure Decisions

Event Store options: EventStoreDB (purpose-built), Apache Kafka (if already in stack), PostgreSQL with append-only tables (simple start), AWS DynamoDB Streams.

Projection rebuild strategy: You will need to rebuild projections. Design for it from day one. Track projection version, support parallel rebuilds, and test rebuild time under production data volumes.

Snapshotting: For aggregates with long event histories (1000+ events), periodic snapshots prevent slow aggregate loading. Balance snapshot frequency against storage cost.

Resources