Khi bạn đặt 1 network call giữa 2 components, MỌI THỨ thay đổi. Session này không dạy tools, mà dạy tư duy để ra quyết định đúng khi thiết kế distributed systems.
Mục tiêu
- Hiểu Fallacies of Distributed Computing (8 giả định sai)
- CAP theorem - thực sự nghĩa là gì
- Eventual Consistency - sống chung thế nào
- Idempotency - tại sao BẮT BUỘC
1) 8 Fallacies - Những Giả Định Sai
Khi viết monolith, bạn NGẦM giả định:
1. Network luôn available → ❌ Network FAIL (timeout, partition)
2. Latency = zero → ❌ Network call = 1-100ms (vs in-process = 0.001ms)
3. Bandwidth vô hạn → ❌ Transfer 10MB JSON giữa services = chậm
4. Network an toàn → ❌ MITM, sniffing, tampering
5. Topology không đổi → ❌ Services di chuyển, IP đổi, scale in/out
6. Có 1 admin duy nhất → ❌ Nhiều team, nhiều services, khác deploy cycle
7. Transport cost = 0 → ❌ Serialize/deserialize, encryption, DNS lookup
8. Network đồng nhất → ❌ Khác region, khác latency, khác bandwidth
→ Mỗi network call là 1 ĐIỂM FAILURE TIỀM TÀNG
→ Monolith: method call LUÔN thành công (cùng process)
→ Microservice: HTTP call CÓ THỂ fail (bất cứ lúc nào)
2) CAP Theorem - Simplified
CAP: trong distributed system, bạn chỉ chọn được 2 trong 3:
C (Consistency): mọi node trả về data GIỐNG NHAU tại cùng thời điểm
A (Availability): mọi request đều nhận response (không timeout)
P (Partition Tolerance): system hoạt động khi network bị partition
P luôn xảy ra trong distributed systems → bạn thực sự chọn giữa C và A:
CP (Consistent + Partition Tolerant):
- Khi partition xảy ra → system reject writes (unavailable)
- Ví dụ: bank account balance (KHÔNG ĐƯỢC sai)
- Tech: Azure SQL (strong consistency)
AP (Available + Partition Tolerant):
- Khi partition xảy ra → system vẫn serve, nhưng data có thể stale
- Ví dụ: product catalog (OK nếu hiện price cũ vài giây)
- Tech: Cosmos DB (eventual consistency)
Thực tế: Chọn theo USE CASE
STRONG consistency khi:
✅ Tiền bạc (balance, transactions)
✅ Inventory (stock count → overselling)
✅ Auth (quyền thay đổi → phải apply ngay)
EVENTUAL consistency khi:
✅ Product catalog (price update delay vài giây = OK)
✅ Notifications (email gửi trễ vài giây = OK)
✅ Analytics (số liệu delay vài phút = OK)
✅ Search index (Elasticsearch sync delay = OK)
3) Eventual Consistency - Cách Sống Chung
Eventual Consistency nghĩa là:
"Data CUỐI CÙNG sẽ consistent, nhưng CÓ THỂ không consistent NGAY LẬP TỨC."
Ví dụ: User đặt hàng
1. Order Service: tạo order (ngay lập tức)
2. Inventory Service: giảm stock (sau 200ms)
3. Email Service: gửi confirmation (sau 2 giây)
4. Analytics: cập nhật report (sau 5 phút)
→ Giữa step 1 và step 2: stock CHƯA giảm → có thể oversell!
Strategies
csharp// Strategy 1: Saga - compensate nếu step sau fail
// Order created → Reserve stock → Charge payment
// Nếu payment fail → Release stock → Cancel order
// Strategy 2: Optimistic UI - hiển thị trước, sync sau
// "Order placed successfully!" → hiển thị NGAY
// Backend vẫn đang xử lý stock, payment...
// Nếu fail → thông báo sau (email, push notification)
// Strategy 3: Reservation - lock tạm thời
// "Giữ hàng 15 phút" → Stock giảm tạm
// Payment thành công → Confirm reservation
// Timeout → Release reservation
4) Idempotency - Tại Sao BẮT BUỘC
Vấn đề: Network unreliable → retry là BẮT BUỘC
Nhưng retry có thể gọi operation 2+ lần!
Client: "Charge $100" → Network timeout
Client: "Charge $100" → retry
Server: nhận 2 lần → charge $200? 💀
Idempotent = gọi N lần, kết quả = gọi 1 lần.
Cách implement
csharp// 1. Idempotency Key (phổ biến nhất)
[HttpPost("payments")]
public async Task<IActionResult> ProcessPayment(
[FromHeader(Name = "Idempotency-Key")] string idempotencyKey,
PaymentRequest request)
{
// Check: đã xử lý key này chưa?
var existing = await db.ProcessedRequests
.FirstOrDefaultAsync(r => r.Key == idempotencyKey);
if (existing is not null)
return Ok(existing.Response); // Trả response cũ, KHÔNG xử lý lại
// Process payment...
var result = await ProcessPaymentAsync(request);
// Lưu kết quả với key
db.ProcessedRequests.Add(new ProcessedRequest
{
Key = idempotencyKey,
Response = JsonSerializer.Serialize(result),
ProcessedAt = DateTime.UtcNow
});
await db.SaveChangesAsync();
return Ok(result);
}
// 2. Natural Idempotency (thiết kế operation tự thân idempotent)
// ❌ Non-idempotent: UPDATE balance = balance + 100
// ✅ Idempotent: UPDATE balance = 1100 WHERE version = 5
// → Gọi 2 lần: lần 2 version đã là 6 → KHÔNG match → KHÔNG update
// 3. Upsert pattern
// ❌ Non-idempotent: INSERT INTO Orders...
// ✅ Idempotent:
// INSERT INTO Orders (Id, ...) VALUES (@id, ...)
// ON CONFLICT (Id) DO NOTHING
5) Distributed Transactions - Tại Sao Khó
Monolith:
BEGIN TRANSACTION
Debit Account A
Credit Account B
COMMIT
→ Cả 2 thành công hoặc cả 2 rollback ✅
Distributed:
Service A: Debit Account A ✅
Network call...
Service B: Credit Account B ❌ (service down!)
→ Account A bị trừ, Account B KHÔNG được cộng! 💀
→ KHÔNG CÓ distributed COMMIT!
Solutions (từ simple → complex)
1. Tránh distributed transaction (tốt nhất!)
→ Đặt related operations CÙNG 1 service
→ Monolith hoặc Modular Monolith
2. Saga pattern (phổ biến)
→ Compensating actions: nếu B fail → undo A
→ Eventually consistent
3. Outbox pattern (reliable publishing)
→ Save entity + event trong CÙNG transaction
→ Background worker publish event
4. 2PC - Two Phase Commit (TRÁNH trong microservices)
→ Blocking, single point of failure
→ Chỉ dùng cho databases cùng instance
6) Decision Framework
Bạn đang xem xét tách service? Hãy hỏi:
1. HAI component này CÓ CẦN consistent CÙNG LÚC không?
→ YES → CÙNG service/transaction
→ NO → Tách OK, eventual consistency
2. HAI component có CÙNG deploy cycle không?
→ YES → Có thể giữ cùng service
→ NO → Tách giúp deploy independent
3. HAI component có KHÁC team ownership không?
→ YES → Tách giúp autonomy
→ NO → Không bắt buộc tách
4. Cost of distributed system chấp nhận được?
→ Network failures, eventual consistency, monitoring overhead
→ Nếu không → giữ monolith/modular monolith
Master Checklist ✅
□ Mỗi network call CÓ retry + timeout?
□ Operations idempotent? (retry safe)
□ Eventual consistency chấp nhận được cho use case này?
□ Saga/Compensation cho distributed workflows?
□ Outbox pattern cho reliable event publishing?
□ Circuit breaker cho external calls?
□ Health checks cho service dependencies?
□ Fallback strategy khi dependency down?
Tư duy master: "Đừng distribute cho đến khi bạn PHẢI distribute." Monolith đúng cách tốt hơn microservices sai cách. Khi bạn tách service, hãy hỏi: "Tôi có chấp nhận COST (network failure, eventual consistency, operational complexity) không?"