Chuyển đến nội dung chính

Lesson 9: Event Sourcing & CQRS — When to need it, when not to?

Event Sourcing: stores events instead of state. CQRS: separate read/write models. Combine Event Sourcing + CQRS. Trade-offs, complexity, and decision framework. When is CQRS too complicated, when is it really necessary?

🏗️ Architecture — Lesson 9 Lesson 9: Event Sourcing & CQRS — When need, when not?

Microservices & Micro Frontend system design — From basics to Production

Part 3: Data Architecture in Microservices

xdev.asia

Introduction

Event Sourcing and CQRS are two powerful but often abused patterns. This article will help you understand its nature, real benefits, and most importantly — when NOT to use it.

CQRS and Event Sourcing — separate Command and Query


1. Event Sourcing

1.1 Core ideas

Instead of saving the current state, save the sequence of events that occurred:

Traditional (State-based):
┌─────────────────────────┐
│ orders table            │
│ id: 123                 │
│ status: SHIPPED   ← chỉ biết trạng thái hiện tại
│ total: 500.000         │
└─────────────────────────┘

Event Sourcing:
┌─────────────────────────────────────┐
│ Event Store (Order #123)            │
│ 1. OrderCreated    {items, total}   │
│ 2. PaymentReceived {amount}         │
│ 3. ItemRemoved     {itemId}   ← biết toàn bộ lịch sử
│ 4. OrderConfirmed  {}               │
│ 5. OrderShipped    {trackingId}     │
└─────────────────────────────────────┘

State = replay(events) → current state

1.2 Benefits

  • Complete audit trail: know exactly who did what and when
  • Time travel: rebuild state at any time
  • Debug: replay events to reproduce bugs
  • Analytics: analyze event patterns

1.3 Disadvantages

  • Complexity: needs event store, projections, snapshots
  • Eventual consistency: read model not real-time
  • Schema evolution: changing event schema is very difficult
  • Difficult query: cannot SELECT * FROM orders WHERE status = 'SHIPPED'

2. CQRS (Command Query Responsibility Segregation)

2.1 Separating Read and Write

Traditional:
┌──────────┐     ┌──────────┐
│  Client  │────►│ Service  │────► Database
│          │◄────│(CRUD all)│◄──── (1 model)
└──────────┘     └──────────┘

CQRS:
                 ┌──────────────┐     ┌────────────┐
           ────► │ Command Side │────►│ Write DB   │
┌──────────┐     │ (Create,     │     │ (optimized │
│  Client  │     │  Update)     │     │  for write)│
└──────────┘     └──────────────┘     └─────┬──────┘
           ────► ┌──────────────┐           │ Events
                 │ Query Side   │     ┌─────▼──────┐
           ◄──── │ (Read,       │◄────│ Read DB    │
                 │  Search)     │     │ (optimized │
                 └──────────────┘     │  for read) │
                                      └────────────┘

2.2 When is CQRS valuable?

  • Read/Write ratio very different (90% read, 10% write)
  • Read model needs different format write model (denormalized views)
  • Need to scale read/write independently (add read replicas)
  • Complex queries need optimized read models (Elasticsearch, materialized views)

3. Decision Framework

3.1 When to USE Event Sourcing + CQRS?

✅ Financial systems (need audit trail) ✅ Order processing (complicated status, needs history) ✅ Collaborative editing (conflict resolution) ✅ Regulatory compliance (need to prove what happened)

3.2 When NOT TO USE?

❌ Simple CRUD (blog, user profile) → overkill ❌ Small team, no experience → learning curve is too high ❌ No need to audit trail or time travel ❌ Real-time consistency is required

3.3 Can use CQRS WITHOUT Event Sourcing

CQRS without Event Sourcing (pragmatic approach):

Write Side: PostgreSQL (normalized)
  → On write: publish event to Kafka
  
Read Side: Elasticsearch (denormalized)
  → Consumer: listen events → update search index

Đơn giản hơn nhiều, vẫn có lợi ích tách read/write.

4. Apply to E-Commerce

Product Service: CQRS (no Event Sourcing)
  Write: PostgreSQL (products table)
  Read:  Elasticsearch (search index, facets)
  Sync:  ProductUpdated event → ES consumer

Order Service: Event Sourcing + CQRS (trạng thái phức tạp)
  Event Store: PostgreSQL (events table)
  Read Model: PostgreSQL (materialized orders view)
  
Cart Service: Simple CRUD (Redis)
  Không cần CQRS — read/write model giống nhau

User Service: Simple CRUD (PostgreSQL)
  Không cần CQRS — straightforward CRUD

Summary

PatternComplexityWhen to useWhen to avoid
Simple CRUDLowMost servicesHigh-traffic reads
CQRS onlyMediumSeparate read/write scaleSimple domains
Event SourcingHighAudit trail, time travelSimple CRUD
ES + CQRSVery HighFinancial, orderingAlmost everything else

Golden rule: Start simple (CRUD). Only add CQRS/ES when a specific pain point proves necessary.


Next article: Lesson 10: What is Micro Frontend? — Benefits, Trade-offs & Decision Framework