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.

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
| Pattern | Complexity | When to use | When to avoid |
|---|---|---|---|
| Simple CRUD | Low | Most services | High-traffic reads |
| CQRS only | Medium | Separate read/write scale | Simple domains |
| Event Sourcing | High | Audit trail, time travel | Simple CRUD |
| ES + CQRS | Very High | Financial, ordering | Almost 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