Introduction
In Monolith, transactions are simple: BEGIN → INSERT order → UPDATE inventory → COMMIT. In Microservices, each service has its own DB → cannot use distributed ACID transactions (2PC is too slow, fragile). Saga Pattern is the standard solution.

1. Problem: Distributed Transactions
1.1 Why is 2PC (Two-Phase Commit) not suitable?
- Blocking: all participants are locked until the coordinator decides
- Single point of failure: coordinator crash → deadlock
- Performance hit: latency increases significantly
- No scale: add participant = add complexity exponential
1.2 Accept Eventual Consistency
In Microservices, we accept eventual consistency instead of strong consistency. The data will be consistent eventually, not immediately.
2. Saga Pattern
Saga = string of local transactions, each transaction updates 1 service. If a transaction fails, the compensating transactions undo previous changes.
2.1 Choreography-based Saga
Each service publish event → the next service listens and acts:
Order Service Payment Service Inventory Service
│ │ │
│──OrderCreated───────►│ │
│ │──PaymentProcessed───►│
│ │ │──InventoryReserved──►
│ │ │
│ (nếu Inventory fail) │
│ │◄──InventoryFailed────│
│◄──PaymentRefunded────│ │
│──OrderCancelled │ │
Advantages: Simple, loosely coupled, no central coordinator Disadvantages: Hard to track overall flow, cyclic dependencies possible
2.2 Orchestration-based Saga
Saga Orchestrator coordinates the entire flow:
┌─────────────────┐
│ Order Saga │
│ Orchestrator │
└──┬──┬──┬───────┘
│ │ │
┌──────────┘ │ └──────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Payment │ │Inventory │ │ Shipping │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
Orchestrator:
1. Create Order (PENDING)
2. Command: ProcessPayment → Payment Service
3. If success: Command: ReserveInventory → Inventory Service
4. If success: Command: CreateShipment → Shipping Service
5. If any fail: Compensate previous steps in reverse
Advantages: Clear flow, easy to understand, centralized error handling Disadvantages: Orchestrator can become bottleneck, single point of failure
2.3 When to use what?
| Criteria | Choreography | Orchestration |
|---|---|---|
| Complexity | 2-3 steps | 4+ steps |
| Visibility | Difficult to track | Clear |
| Coupling | Loose | Medium (to orchestrator) |
| Error handling | Distributed | Centralized |
3. Compensating Transactions
Compensating transaction = "undo" for business transaction (not DB rollback):
Forward: Compensating:
CreateOrder → CancelOrder
ProcessPayment → RefundPayment
ReserveInventory → ReleaseInventory
CreateShipment → CancelShipment
Note: Compensating transaction must be idempotent — called multiple times with the same result.
4. Idempotency — Handling Duplicate Messages
In a distributed system, messages can be sent multiple times (at-least-once delivery). Service must handle duplicates:
// Idempotent payment processing
async function processPayment(command) {
// Check idempotency key
const existing = await db.findPayment(command.idempotencyKey);
if (existing) return existing; // Already processed
// Process payment
const result = await stripe.charge(command.amount);
await db.savePayment({
idempotencyKey: command.idempotencyKey,
...result
});
return result;
}
5. Hands-on: E-Commerce Order Saga
Create Order Saga (Orchestration):
Step 1: Order Service → CreateOrder(PENDING)
Step 2: Payment Service → ChargeCustomer(orderId, amount)
✅ → Step 3
❌ → Compensate: CancelOrder → DONE (Order: PAYMENT_FAILED)
Step 3: Inventory Service → ReserveItems(orderId, items)
✅ → Step 4
❌ → Compensate: RefundPayment → CancelOrder → DONE (Order: OUT_OF_STOCK)
Step 4: Order Service → ConfirmOrder(orderId)
✅ → DONE (Order: CONFIRMED)
Step 5 (async): Notification Service → SendConfirmationEmail
Summary
- 2PC is not suitable for Microservices — too slow and fragile
- Saga Pattern = sequence of local transactions + compensating transactions
- Choreography: simple flows (2-3 steps), loose coupling
- Orchestration: complex flows (4+ steps), clear visibility
- Idempotency is required — always handle duplicate messages
Next article: Lesson 9: Event Sourcing & CQRS — When to need it, when not to?