Tuần 11: Microservices Patterns

“Microservices không phải là đích đến. Nó là một chiến lược tổ chức hệ thống — và nếu dùng sai thời điểm, em sẽ có một distributed monolith: phức tạp của microservices nhưng không có lợi ích nào.”

Tags: system-design microservices architecture devops security alex-xu Student: Hieu Prerequisite: Tuan-04-API-Design-REST-gRPC · Tuan-08-Message-Queue · Tuan-05-Load-Balancer Liên quan: Tuan-12-CICD-Pipeline · Tuan-13-Monitoring-Observability · Tuan-14-AuthN-AuthZ-Security · Tuan-15-Data-Security-Encryption


1. Context & Why

Analogy đời thường

Hieu, tưởng tượng em đang quản lý một nhà hàng:

Monolith = Nhà hàng một bếp duy nhất

  • Một bếp nấu tất cả: phở, cơm tấm, pizza, sushi
  • Nếu bếp hỏng → cả nhà hàng đóng cửa
  • Muốn thêm món mới → phải sửa cả layout bếp
  • Mọi đầu bếp phải biết nấu mọi thứ
  • Khi khách đông, không thể mở rộng riêng phần bếp phở

Microservices = Food court nhiều quầy chuyên biệt

  • Quầy phở, quầy cơm tấm, quầy pizza — mỗi quầy tự vận hành
  • Quầy phở hỏng → các quầy khác vẫn bán bình thường (fault isolation)
  • Muốn thêm quầy bún bò → dựng thêm một quầy mới, không ảnh hưởng quầy khác (independent deployment)
  • Mỗi quầy có đầu bếp chuyên môn riêng (team autonomy)
  • Quầy phở đông khách → mở thêm 2 quầy phở nữa, không cần mở rộng quầy pizza (independent scaling)

Nhưng food court cũng có chi phí:

  • Cần một quầy thông tin ở cửa vào để hướng dẫn khách → API Gateway
  • Cần hệ thống gọi món trung tâm để phối hợp order liên quầy → Message Queue / Saga
  • Cần camera giám sát toàn bộ food court → Distributed Tracing / Monitoring
  • Cần bảo vệ kiểm tra thẻ giữa các quầy → mTLS / Zero Trust

Tại sao đây là tuần 11?

Vì trước khi hiểu microservices, Hieu cần nắm vững:

  • API Design (Tuần 4) — cách services giao tiếp
  • Load Balancer (Tuần 5) — cách phân tải giữa instances
  • Message Queue (Tuần 8) — nền tảng async communication
  • Consistent Hashing (Tuần 10) — cách phân phối data/traffic

Microservices không phải là bước đầu tiên. Theo Alex Xu: “Start with monolith, evolve to microservices when complexity demands it.”


2. Deep Dive — Kiến trúc Microservices toàn diện

2.1 Monolith vs Microservices — Trade-offs chi tiết

Tiêu chíMonolithMicroservices
DeploymentDeploy cả hệ thống mỗi lầnDeploy từng service độc lập
ScalingScale toàn bộ app (vertical/horizontal)Scale từng service riêng biệt
CodebaseMột repo duy nhất (hoặc monorepo)Multi-repo hoặc monorepo + boundaries
TechnologyMột tech stack duy nhấtMỗi service có thể dùng tech stack khác
DataMột database chungDatabase per service (lý tưởng)
TeamTất cả dev làm chung codebaseMỗi team sở hữu 1-2 services
TestingIntegration test đơn giản (in-process)Cần contract testing, E2E phức tạp
DebuggingStack trace rõ ràngDistributed tracing (Jaeger, Zipkin)
LatencyFunction call = nanosecondsNetwork call = milliseconds
ConsistencyACID transactions dễ dàngEventual consistency, Saga pattern
Operational costThấp (1 app, 1 DB, 1 deploy pipeline)Cao (N apps, N DBs, N pipelines)

Khi nào NÊN dùng Microservices?

  1. Team > 10-15 developers, bắt đầu “đạp chân nhau” trên codebase
  2. Cần deploy các phần khác nhau với tần suất khác nhau
  3. Cần scale riêng biệt (ví dụ: search service cần 20 instances, user service chỉ cần 3)
  4. Cần fault isolation thực sự (một phần chết không kéo cả hệ thống)
  5. Codebase đã quá lớn (> 500K LOC), build time > 30 phút

Khi nào KHÔNG nên?

  1. Team nhỏ (< 5 devs) — overhead vận hành sẽ “ăn” hết productivity
  2. Product chưa rõ domain boundaries — tách sai rất đau
  3. Chưa có DevOps maturity (CI/CD, monitoring, containerization)
  4. Đang ở giai đoạn MVP — tốc độ phát triển quan trọng hơn scalability

2.2 Service Decomposition Strategies — Cách tách service

Strategy 1: Decompose by Business Domain (theo nghiệp vụ)

Đây là cách phổ biến nhất, dựa trên Bounded Context của DDD (Domain-Driven Design):

E-commerce Platform
├── User Service          → Quản lý tài khoản, profile
├── Product Service       → Catalog, inventory
├── Order Service         → Đặt hàng, trạng thái đơn
├── Payment Service       → Thanh toán, hoàn tiền
├── Shipping Service      → Vận chuyển, tracking
├── Notification Service  → Email, SMS, push notification
├── Search Service        → Full-text search, recommendations
└── Review Service        → Đánh giá, bình luận

Nguyên tắc: Mỗi service map 1:1 với một business capability (khả năng nghiệp vụ). Nếu em tách theo technical layers (một service cho “database layer”, một cho “business logic layer”) → đó là sai, vì mỗi thay đổi nghiệp vụ sẽ cần sửa nhiều services.

Strategy 2: Decompose by Subdomain (DDD approach)

DDD phân chia domain thành 3 loại subdomain:

LoạiĐặc điểmVí dụ (E-commerce)Chiến lược
Core DomainLợi thế cạnh tranh, phức tạp nhấtRecommendation Engine, Pricing EngineBuild in-house, đầu tư nhiều nhất
Supporting DomainHỗ trợ core, không phải USPOrder Management, InventoryBuild hoặc customize off-the-shelf
Generic DomainChung cho mọi businessAuthentication, Payment Gateway, NotificationBuy/use SaaS (Auth0, Stripe, Twilio)

Context Mapping — cách các Bounded Context giao tiếp:

┌──────────────────┐     Shared Kernel     ┌──────────────────┐
│  Order Context    │◄────────────────────►│  Payment Context  │
│                   │                       │                   │
│  - Order          │   Anti-Corruption     │  - Transaction    │
│  - OrderItem      │       Layer           │  - Refund         │
│  - OrderStatus    │◄─────────────────────│  - PaymentMethod  │
└──────────────────┘                       └──────────────────┘
        │                                           │
        │  Customer/Supplier                        │  Conformist
        ▼                                           ▼
┌──────────────────┐                       ┌──────────────────┐
│ Shipping Context  │                       │ External Payment  │
│                   │                       │ Provider (Stripe) │
│  - Shipment       │                       │                   │
│  - Carrier        │                       │  (Conform to their│
│  - TrackingInfo   │                       │   API contract)   │
└──────────────────┘                       └──────────────────┘

Anti-Corruption Layer (ACL): Khi service A giao tiếp với service B có model khác biệt nhiều, ACL đóng vai trò “phiên dịch” để model bên trong service A không bị “nhiễm” model của B. Rất quan trọng khi integrate với legacy system hoặc third-party.

2.3 Inter-Service Communication — Giao tiếp giữa các services

Synchronous Communication (đồng bộ)

REST over HTTP

Order Service --HTTP POST--> Payment Service
              <--200 OK-----
Ưu điểmNhược điểm
Đơn giản, dễ hiểuTight coupling (caller chờ response)
Tooling phong phú (Postman, Swagger)Cascading failure (A → B → C, C chết → tất cả chết)
Dễ debugLatency cộng dồn qua mỗi hop
StatelessRetry logic phức tạp (idempotency)

gRPC (Google Remote Procedure Call)

Order Service --gRPC (HTTP/2, Protobuf)--> Payment Service
              <--Binary response-----------
Ưu điểmNhược điểm
Nhanh hơn REST 2-10x (binary serialization)Learning curve cao hơn
Contract-first (.proto files)Không đọc được bằng mắt (binary)
Bi-directional streamingBrowser support hạn chế (cần grpc-web)
Code generation tự độngDebugging khó hơn REST
HTTP/2 multiplexingLoad balancing phức tạp hơn (cần L7 LB)

Khi nào dùng gRPC vs REST?

  • gRPC: Service-to-service internal communication, high-throughput, streaming data
  • REST: Public APIs, browser clients, khi cần human-readable format

Asynchronous Communication (bất đồng bộ)

Message Queue (Event-Driven)

Order Service --publish "OrderCreated"--> Message Broker (Kafka/RabbitMQ)
                                              │
                                              ├──> Payment Service (subscribe)
                                              ├──> Inventory Service (subscribe)
                                              └──> Notification Service (subscribe)
Ưu điểmNhược điểm
Loose coupling (producer không biết consumer)Eventual consistency (không tức thì)
Fault tolerance (queue buffer khi consumer chết)Debugging phức tạp (message tracing)
Scale independentlyMessage ordering challenges
Không cascading failureInfrastructure thêm (broker cluster)
Natural load levelingDuplicate message handling (idempotency)

Chi tiết về Message Queue: Tuan-08-Message-Queue

Hybrid approach (thực tế phổ biến nhất):

  • Sync cho queries cần response ngay (GET user profile, GET product details)
  • Async cho commands/events (place order, send notification, update inventory)

2.4 API Gateway Pattern

API Gateway là single entry point cho tất cả client requests, đóng vai trò “quầy lễ tân” của food court.

Responsibilities:

                         ┌─────────────────────────────────────┐
                         │           API Gateway               │
   Mobile App ──────────►│                                     │
   Web App ─────────────►│  1. Request Routing                 │
   3rd Party ───────────►│  2. Authentication/Authorization    │
                         │  3. Rate Limiting                   │
                         │  4. SSL Termination                 │
                         │  5. Load Balancing                  │
                         │  6. Request/Response Transformation │
                         │  7. Response Caching                │
                         │  8. Circuit Breaking                │
                         │  9. Logging & Monitoring            │
                         │ 10. API Versioning                  │
                         └──────────┬──────────────────────────┘
                                    │
                    ┌───────────────┼───────────────┐
                    ▼               ▼               ▼
              User Service    Order Service   Product Service

Công nghệ phổ biến: Kong, AWS API Gateway, Nginx (with Lua), Envoy, Traefik, APISIX

Lưu ý: API Gateway có thể trở thành single point of failureperformance bottleneck. Cần:

  • Deploy nhiều instances + Load Balancer phía trước
  • Keep gateway logic thin — chỉ routing, auth, rate limit
  • Không đặt business logic vào gateway

2.5 Backend for Frontend (BFF) Pattern

Khi có nhiều loại client (mobile, web, IoT) với nhu cầu data khác nhau:

                  ┌─────────────┐
   Mobile App ───►│  Mobile BFF  │───┐
                  └─────────────┘   │
                  ┌─────────────┐   │    ┌──────────────┐
   Web App ──────►│   Web BFF    │───┼───►│ User Service  │
                  └─────────────┘   │    ├──────────────┤
                  ┌─────────────┐   │    │ Order Service │
   IoT Device ──►│   IoT BFF    │───┘    ├──────────────┤
                  └─────────────┘        │Product Service│
                                         └──────────────┘

Tại sao cần BFF?

  • Mobile cần payload nhỏ (bandwidth hạn chế) → BFF mobile chỉ trả fields cần thiết
  • Web cần data phong phú hơn → BFF web aggregates nhiều services
  • IoT cần protocol khác (MQTT) → BFF IoT chuyển đổi protocol

BFF vs API Gateway: API Gateway là shared infrastructure (routing, auth). BFF là application-level (data aggregation, transformation cho từng loại client). Thường deploy: Client → API Gateway → BFF → Microservices.

2.6 Saga Pattern — Distributed Transactions

Trong monolith, một order flow đơn giản:

BEGIN TRANSACTION;
  INSERT INTO orders (...);
  UPDATE inventory SET quantity = quantity - 1;
  INSERT INTO payments (...);
COMMIT;

Trong microservices, mỗi service có database riêng → không thể dùng ACID transaction xuyên services. Saga pattern giải quyết bằng cách chia transaction thành chuỗi local transactions với compensating transactions (hoàn tác) cho mỗi bước.

Choreography-based Saga (phi tập trung)

Mỗi service tự lắng nghe events và quyết định bước tiếp theo:

Order Service                  Payment Service              Inventory Service
     │                              │                              │
     │──"OrderCreated"──────────►   │                              │
     │                              │──"PaymentProcessed"────────► │
     │                              │                              │──"InventoryReserved"──►
     │                              │                              │
     │   (Nếu Payment fail)         │                              │
     │◄──"PaymentFailed"───────────│                              │
     │──"OrderCancelled"──────────►│                              │
     │                              │──"PaymentRefunded"─────────►│
     │                              │                              │──"InventoryReleased"──►
Ưu điểmNhược điểm
Đơn giản khi ít bước (2-3 services)Khó theo dõi flow khi nhiều services
Loose couplingCyclic dependencies có thể xảy ra
Không có single point of failureTesting phức tạp
Mỗi service tự chủKhông có global view của transaction

Orchestration-based Saga (tập trung)

Một Saga Orchestrator điều phối toàn bộ flow:

                    Saga Orchestrator
                          │
           ┌──────────────┼──────────────┐
           ▼              ▼              ▼
      Order Service  Payment Service  Inventory Service

  Step 1: CreateOrder()
  Step 2: ProcessPayment()
  Step 3: ReserveInventory()

  If Step 3 fails:
    Compensate 2: RefundPayment()
    Compensate 1: CancelOrder()
Ưu điểmNhược điểm
Dễ theo dõi flow (centralized logic)Orchestrator là single point of failure
Dễ thêm/sửa stepsRisk: orchestrator chứa quá nhiều logic
Tránh cyclic dependenciesCoupling vào orchestrator
Dễ test (test orchestrator = test flow)Cần manage state machine

Khi nào dùng gì?

  • Choreography: 2-4 services, flow đơn giản, team muốn loose coupling tối đa
  • Orchestration: > 4 services, flow phức tạp, cần visibility rõ ràng, cần compensating logic phức tạp

2.7 Circuit Breaker Pattern

Khi service B chết, service A vẫn gọi liên tục → waste resources + cascade failure. Circuit Breaker ngăn chặn điều này, giống cầu dao điện trong nhà:

State Machine:

        ┌─────────────────────────────────┐
        │                                 │
        ▼                                 │
   ┌─────────┐    failure threshold   ┌───┴─────┐
   │  CLOSED  │──────────────────────►│  OPEN    │
   │(cho phép)│   (vượt ngưỡng lỗi)  │(chặn hết)│
   └─────────┘                        └────┬────┘
        ▲                                  │
        │     success                      │  timeout
        │                                  ▼
        │                           ┌───────────┐
        └───────────────────────────│ HALF-OPEN  │
              (thử lại thành công)  │(cho 1 vài) │
                                    └───────────┘
                                         │
                                         │ failure
                                         ▼
                                    ┌─────────┐
                                    │  OPEN    │
                                    └─────────┘

3 trạng thái:

  1. CLOSED (bình thường): Request đi qua bình thường. Đếm số lỗi.
  2. OPEN (cắt mạch): Khi lỗi vượt ngưỡng (ví dụ: 5 lỗi trong 10 giây), ngắt mạch. Mọi request trả về fallback response ngay lập tức — không gọi service B.
  3. HALF-OPEN (thăm dò): Sau timeout (ví dụ: 30 giây), cho phép 1-2 request thử. Nếu thành công → CLOSED. Nếu thất bại → OPEN lại.

Fallback strategies:

  • Trả về cached data (stale but available)
  • Trả về default response
  • Trả về error message thân thiện
  • Redirect sang service backup

2.8 Bulkhead Pattern

Tên gọi lấy từ vách ngăn kín nước trên tàu biển — nếu một khoang bị thủng, nước chỉ ngập khoang đó, không lan sang khoang khác.

Trong microservices, Bulkhead cô lập resources để failure ở một phần không ảnh hưởng phần khác:

┌──────────────────────────────────────────────┐
│              Order Service                    │
│                                              │
│  ┌──────────────┐   ┌──────────────┐         │
│  │ Thread Pool A │   │ Thread Pool B │         │
│  │ (10 threads)  │   │ (10 threads)  │         │
│  │               │   │               │         │
│  │ → Payment API │   │ → Inventory   │         │
│  │               │   │   API         │         │
│  └──────────────┘   └──────────────┘         │
│                                              │
│  Nếu Payment API chết, chỉ Pool A bị block  │
│  Pool B (Inventory) vẫn hoạt động bình thường│
└──────────────────────────────────────────────┘

Cách implement Bulkhead:

  1. Thread pool isolation: Mỗi downstream dependency có thread pool riêng
  2. Connection pool isolation: Giới hạn connection tới mỗi dependency
  3. Semaphore isolation: Giới hạn concurrent calls (nhẹ hơn thread pool)
  4. Pod/Container isolation: Mỗi function group chạy trên pod riêng

2.9 Service Discovery — Tìm kiếm services

Trong microservices, services scale dynamically (thêm/bớt instances). Không thể hardcode IP. Service Discovery giải quyết vấn đề: “Service A muốn gọi Service B. Service B ở đâu?”

Client-Side Discovery

                      ┌──────────────┐
                      │   Service    │
  Service A ─────────►   │   Registry   │  (Consul, Eureka, etcd)
       │              │              │
       │  1. Query     │ user-svc:    │
       │  "user-svc"    │  - 10.0.1.5  │
       │              │  - 10.0.1.6  │
       │              │  - 10.0.1.7  │
       │              └──────────────┘
       │
       │  2. Client tự chọn instance (round-robin, random, etc.)
       │
       └──────────────────────────────►  10.0.1.6 (User Service)
  • Ưu điểm: Ít hops hơn, client có thể chọn strategy tùy ý
  • Nhược điểm: Client phải implement discovery logic, mỗi ngôn ngữ phải implement riêng

Server-Side Discovery

                      ┌──────────────┐
  Service A ─────────►│ Load Balancer│─────────► 10.0.1.6 (User Service)
                      │  / Router    │
                      └──────┬───────┘
                             │ query
                      ┌──────▼───────┐
                      │   Service    │
                      │   Registry   │
                      └──────────────┘
  • Ưu điểm: Client đơn giản (chỉ cần biết LB address), language-agnostic
  • Nhược điểm: Thêm một hop (latency), LB có thể là bottleneck

Công cụ phổ biến:

ToolTypeHealth CheckKey-Value StoreGhi chú
Consul (HashiCorp)BothHTTP, TCP, gRPC, ScriptYesMulti-datacenter native
Eureka (Netflix)Client-sideHeartbeatNoJava ecosystem, Spring Cloud
etcdServer-side (via Kubernetes)YesKubernetes native
ZooKeeperServer-sideSession-basedYesLegacy, đang bị thay thế

2.10 Service Mesh — Istio / Linkerd

Service Mesh là infrastructure layer xử lý service-to-service communication, giúp developers không phải implement cross-cutting concerns (retry, circuit breaker, mTLS, observability) trong application code.

Sidecar Proxy Pattern

┌─────────────────────────┐     ┌─────────────────────────┐
│         Pod A           │     │         Pod B           │
│ ┌─────────┐ ┌─────────┐│     │┌─────────┐ ┌─────────┐ │
│ │ Service │ │ Sidecar ││     ││ Sidecar │ │ Service │ │
│ │    A    │→│ Proxy A ││────►││ Proxy B │→│    B    │ │
│ │         │ │(Envoy)  ││     ││(Envoy)  │ │         │ │
│ └─────────┘ └─────────┘│     │└─────────┘ └─────────┘ │
└─────────────────────────┘     └─────────────────────────┘

              ▲                          ▲
              │      Control Plane       │
              │    (Istiod / Linkerd)    │
              └──────────┬───────────────┘
                         │
                   ┌─────▼──────┐
                   │  Config:   │
                   │  - Routing │
                   │  - mTLS    │
                   │  - Retry   │
                   │  - CB      │
                   └────────────┘

Mỗi pod có một sidecar proxy (thường là Envoy) chạy bên cạnh application container. Mọi traffic in/out đều đi qua sidecar → sidecar xử lý:

FeatureKhông có Service MeshVới Service Mesh
mTLSTự implement trong codeTự động, transparent
Retry / TimeoutLibrary trong mỗi serviceConfig ở control plane
Circuit BreakerCode-level (Hystrix, opossum)Infrastructure-level
ObservabilityInstrument manuallyTự động metrics, traces
Traffic splittingManual (canary flags)Declarative (90/10 split)
Rate LimitingCode hoặc gatewayPolicy ở mesh level

Istio vs Linkerd:

Tiêu chíIstioLinkerd
ProxyEnvoy (C++)linkerd2-proxy (Rust)
ComplexityCao, nhiều featuresĐơn giản, dễ setup
Resource overheadNhiều hơn (~50-100MB/sidecar)Ít hơn (~20-30MB/sidecar)
Learning curveCaoThấp
FeaturesRất đầy đủCore features, đủ dùng
Best forEnterprise, cần mọi featureStartup/mid-size, cần đơn giản

2.11 Distributed Tracing — Theo dõi request xuyên services

Khi một request đi qua 5-10 services, làm sao biết bottleneck ở đâu?

Request ID: abc-123

API Gateway [2ms]
  └─► Order Service [15ms]
        ├─► User Service [3ms]        ← cache hit, nhanh
        ├─► Product Service [45ms]     ← ⚠️ slow query!
        │     └─► Inventory DB [40ms]  ← ⚠️ root cause
        └─► Payment Service [8ms]
              └─► Stripe API [120ms]   ← external, expected

Total: 193ms

3 pillars of Observability:

  1. Logs: “Chuyện gì đã xảy ra?” — structured logs (JSON) cho mỗi service
  2. Metrics: “Hệ thống đang như thế nào?” — QPS, latency, error rate (RED metrics)
  3. Traces: “Request đi qua đâu, mất bao lâu?” — span-based tracing

OpenTelemetry là standard hiện nay, tích hợp cả 3 pillars. Backend phổ biến:

  • Jaeger (Uber, open-source) — distributed tracing
  • Zipkin (Twitter, open-source) — distributed tracing
  • Grafana Tempo — traces, tích hợp tốt với Grafana stack

Chi tiết: Tuan-13-Monitoring-Observability

2.12 Data Management — Database per Service

Nguyên tắc: Mỗi service sở hữu database riêng

┌──────────┐    ┌──────────┐    ┌──────────┐
│  Order   │    │ Payment  │    │ Product  │
│ Service  │    │ Service  │    │ Service  │
└────┬─────┘    └────┬─────┘    └────┬─────┘
     │               │               │
┌────▼─────┐    ┌────▼─────┐    ┌────▼─────┐
│ Order DB │    │Payment DB│    │Product DB│
│(Postgres)│    │(Postgres)│    │ (Mongo)  │
└──────────┘    └──────────┘    └──────────┘

Tại sao?

  • Loose coupling: Thay đổi schema DB này không ảnh hưởng service khác
  • Independent scaling: Order DB cần sharding, Product DB không cần
  • Tech freedom: Payment dùng PostgreSQL (ACID), Product dùng MongoDB (flexible schema), Search dùng Elasticsearch

Shared Database Anti-Pattern:

┌──────────┐    ┌──────────┐    ┌──────────┐
│  Order   │    │ Payment  │    │ Product  │
│ Service  │    │ Service  │    │ Service  │
└────┬─────┘    └────┬─────┘    └────┬─────┘
     │               │               │
     └───────────────┼───────────────┘
                     │
              ┌──────▼──────┐
              │  Shared DB   │  ← ANTI-PATTERN!
              │  (1 schema)  │
              └─────────────┘

Tại sao shared DB là anti-pattern?

  1. Tight coupling: Service A thay đổi table → Service B bể
  2. Không scale riêng: Bottleneck ở 1 service → cả DB bị ảnh hưởng
  3. Schema conflicts: 2 teams sửa cùng table → merge conflicts
  4. Không polyglot persistence: Mọi service buộc dùng cùng DB technology

Challenge khi database per service: Cần data từ nhiều services → không thể JOIN.

  • API Composition: Service A gọi API Service B, C để lấy data rồi join ở application level
  • CQRS (Command Query Responsibility Segregation): Tách read/write models. Write vào service DB. Read từ denormalized read model (materialized view).
  • Event Sourcing + Change Data Capture: Services publish events khi data thay đổi → các services khác build local copy

2.13 Strangler Fig Pattern — Migration từ Monolith

Không ai nên rewrite monolith từ đầu (“big bang rewrite”). Strangler Fig — đặt tên theo cây đa siết (strangler fig) bao quanh cây chủ rồi dần thay thế:

Phase 1: Monolith handles everything
┌─────────────────────────────────┐
│          Monolith               │
│  [Users] [Orders] [Products]    │
│  [Payments] [Notifications]     │
└─────────────────────────────────┘

Phase 2: Extract one service, route via proxy
┌──────────────────┐
│   API Proxy      │──── /api/notifications ───► Notification Service (new)
│   (Strangler     │
│    Facade)       │──── /api/* ──────────────► Monolith (existing)
└──────────────────┘

Phase 3: Continue extracting
┌──────────────────┐
│   API Proxy      │──── /api/notifications ───► Notification Service
│                  │──── /api/payments ─────────► Payment Service
│                  │──── /api/products ─────────► Product Service
│                  │──── /api/* ──────────────► Monolith (smaller)
└──────────────────┘

Phase 4: Monolith fully replaced
┌──────────────────┐
│   API Gateway    │──── /api/notifications ───► Notification Service
│                  │──── /api/payments ─────────► Payment Service
│                  │──── /api/products ─────────► Product Service
│                  │──── /api/orders ───────────► Order Service
│                  │──── /api/users ────────────► User Service
└──────────────────┘

Nguyên tắc migration:

  1. Bắt đầu từ service ít coupling nhất (thường là Notification, Logging)
  2. Đặt proxy/facade phía trước monolith
  3. Tách từng service một, route traffic qua proxy
  4. Giữ monolith chạy song song → rollback dễ dàng
  5. Khi service mới ổn định → tắt phần tương ứng trong monolith
  6. Lặp lại cho đến khi monolith rỗng

2.14 Modern Service Mesh — Ambient Mesh, Cilium, eBPF

Cập nhật 2024-2026: Sidecar pattern cũ (Istio classic, Linkerd 2.x) đang bị thay thế bởi sidecarless approach. Đây là xu hướng quan trọng nhất cho microservice infrastructure trong 3 năm tới.

2.14.1 Vấn đề của Sidecar truyền thống

Section 2.10 ở trên mô tả sidecar pattern — mỗi pod có 1 Envoy proxy. Trong production, điều này có cost lớn:

Vấn đềChi tiết
Resource overheadCluster 1000 pods → 1000 Envoy sidecars × 50-100MB = 50-100GB RAM riêng cho mesh
Latency taxMỗi request đi qua 2 sidecar (egress + ingress) → +1-2ms per hop
Operational cost1000 sidecar phải được rolling-upgrade khi update Istio
Lifecycle couplingSidecar restart → application traffic disrupt
Sidecar injection complexityWebhook configurations, init containers, namespace labels

Real-world impact: Đây là lý do nhiều công ty (HBO Max, Salesforce) đã bắt đầu rút khỏi Istio classic sau 2-3 năm production và evaluate alternatives.

2.14.2 Istio Ambient Mesh (2023+, GA 2024)

Istio Ambient loại bỏ sidecar bằng cách chia mesh thành 2 layers:

┌─────────────────────────────────────────────────────┐
│   Application Pods (no sidecar!)                     │
│   ┌───────────┐  ┌───────────┐  ┌───────────┐       │
│   │  Service A│  │  Service B│  │  Service C│       │
│   └─────┬─────┘  └─────┬─────┘  └─────┬─────┘       │
└─────────┼──────────────┼──────────────┼─────────────┘
          │              │              │
          ▼              ▼              ▼
   ┌─────────────────────────────────────────┐
   │   Layer 4: ztunnel (per-node DaemonSet) │
   │   - mTLS, identity, basic routing       │
   │   - Always on, zero-overhead            │
   └─────────────────────────────────────────┘
                        │
                        ▼ (only when L7 needed)
   ┌─────────────────────────────────────────┐
   │   Layer 7: waypoint proxy (optional)    │
   │   - HTTP routing, retry, auth policy    │
   │   - Per-namespace, opt-in               │
   └─────────────────────────────────────────┘

Hai layer:

  • L4 (ztunnel) — DaemonSet, 1 pod/node. Handle mTLS + L4 routing. Always on.
  • L7 (waypoint proxy) — Optional, only deploy khi cần L7 features (HTTP routing, retry policies). Per-namespace.

Ưu điểm:

Tiêu chíSidecar MeshAmbient Mesh
Memory per pod+50-100MB0 (no sidecar)
Memory per node0~200MB (1 ztunnel)
Latency tax2 hops sidecar1 hop ztunnel (L4 only)
Pod restart on mesh upgradeRequiredNot required
L7 featuresAlways on (cost)Opt-in (pay-per-use)
Suitable forApps cần mọi L7 featureMixed workload, chi phí matter

Khi nào dùng Ambient:

  • Cluster lớn (1000+ pods) cần giảm overhead
  • Workload phần lớn không cần L7 routing (chỉ cần mTLS)
  • Cần upgrade mesh không disrupt app

Khi nào vẫn dùng Sidecar (classic):

  • Cần L7 features cho mọi request (rate limit, auth, transform)
  • Đã đầu tư heavy vào Envoy filter custom
  • Mature stack, không muốn migration risk

Tham chiếu: https://istio.io/latest/docs/ambient/overview/

2.14.3 Cilium — Mesh trên eBPF (sidecarless from day 1)

Cilium dùng eBPF (extended Berkeley Packet Filter) để implement networking + service mesh trực tiếp trong kernel, không cần sidecar proxy.

┌──────────────────────────────────────────┐
│         Application Pod                    │
│         (no sidecar)                       │
└────────────────┬─────────────────────────┘
                 │
                 ▼ (kernel)
┌──────────────────────────────────────────┐
│   Linux Kernel + eBPF programs            │
│   - Service routing (L4/L7)              │
│   - Network policy enforcement           │
│   - mTLS (with WireGuard or IPsec)       │
│   - Observability (Hubble)               │
│   - Load balancing (replace kube-proxy)  │
└──────────────────────────────────────────┘

eBPF basics:

  • Cho phép run sandboxed code trong kernel mà không cần kernel module
  • Verified at load time → safe
  • Hot-reloadable, no kernel reboot
  • Visibility ở every packet/syscall

So sánh Cilium vs Istio Ambient:

Tiêu chíCiliumIstio Ambient
Data planeeBPF (kernel)ztunnel (userspace)
PerformanceCao nhất (in-kernel)Tốt (user-space proxy)
L7 featuresCilium Service Mesh (Envoy as needed)Waypoint (Envoy)
CNI replacementCó (replace kube-proxy)Không (need separate CNI)
MaturityGA, production-proven (Google GKE Dataplane V2, Datadog)GA mid-2024
Vendor lockCNCF, vendor-neutralIstio community

Real-world adoptions:

  • Google GKE Dataplane V2 — built on Cilium
  • Datadog — Cilium for production observability
  • Adobe — migrated to Cilium for performance

Tham chiếu:

2.14.4 Decision matrix — chọn mesh nào?

Need full L7 features for every service?
├─ YES → Istio classic (sidecar) — proven path
└─ NO  → Need maximum performance + observability?
         ├─ YES → Cilium (eBPF)
         └─ NO  → Istio Ambient (mid-ground)

Anti-pattern: Migrate giữa các mesh thường xuyên. Mỗi mesh là 6-12 tháng investment. Chọn 1 và stick với nó.

2.15 Cell-based Architecture & Shuffle Sharding

Đây là pattern AWS dùng cho hầu hết hệ thống critical (DynamoDB, Lambda, Route 53). Chuẩn industry cho blast radius reduction.

2.15.1 Vấn đề: Blast radius khi 1 failure

Traditional architecture:

                    [Load Balancer]
                          │
       ┌──────────────────┼──────────────────┐
       │                  │                  │
   [Service A1]      [Service A2]      [Service A3]
       │                  │                  │
       └──────────────────┼──────────────────┘
                          │
                  [Single Database]
                          │
                  ⚡ Failure mode ⚡

Khi 1 component fail (DB corruption, bad deployment, hot key, DDoS) → toàn bộ user bị ảnh hưởng.

Real-world incident examples:

  • 2017 AWS S3 outage — typo in command → 4 hours global S3 down → $150M+ damage
  • 2021 Facebook 6-hour outage — BGP misconfiguration → all services down
  • 2024 CrowdStrike — bad update → 8.5M Windows machines crash globally

2.15.2 Cell-based Architecture

Nguyên lý: Chia hệ thống thành N cells độc lập, mỗi cell phục vụ subset users. Failure trong 1 cell → chỉ N% users bị ảnh hưởng.

            [Cellular Router]
                    │
      ┌─────────────┼─────────────┐
      ▼             ▼             ▼
 ┌──────────┐  ┌──────────┐  ┌──────────┐
 │  Cell 1  │  │  Cell 2  │  │  Cell 3  │
 │          │  │          │  │          │
 │ App tier │  │ App tier │  │ App tier │
 │   DB     │  │   DB     │  │   DB     │
 │   Cache  │  │   Cache  │  │   Cache  │
 │          │  │          │  │          │
 │ Users    │  │ Users    │  │ Users    │
 │ A-G      │  │ H-O      │  │ P-Z      │
 └──────────┘  └──────────┘  └──────────┘

Tính chất quan trọng:

  1. Independent: Cell không chia sẻ state với cell khác
  2. Fixed size: Mỗi cell có capacity giới hạn (e.g., 100K users), không scale up beyond
  3. Identical stack: Cell 1 = Cell 2 architecturally, chỉ khác data
  4. Routed deterministically: Mỗi user thuộc 1 cell cố định (theo user_id hash)

Failure isolation:

  • Cell 1 DB corruption → chỉ users A-G bị ảnh hưởng (33%)
  • Bad deployment trong Cell 1 → blue-green test, rollback chỉ affects 33%
  • Hot key trong Cell 1 → không lan sang Cell 2/3

2.15.3 Cell sizing trade-off

Cell sizeBlast radiusOperational overhead
1M users/cell100% impact nếu failMinimal — như monolith
100K users/cell10% impactManageable
10K users/cell1% impactCao (10x infrastructure)
1K users/cell0.1% impactRất cao

AWS standard: 100K-1M units per cell. Optimize cho:

  • Big enough để efficient (DB economy of scale)
  • Small enough để failure không catastrophic

2.15.4 Shuffle Sharding — Tăng isolation

Vấn đề: Với cell-based, nếu 1 user “bad actor” làm Cell A overload → mọi user trong Cell A bị ảnh hưởng (10% blast).

Shuffle Sharding (AWS pattern): Mỗi user được assign subset of resources thay vì toàn bộ 1 cell.

Ví dụ: 8 worker nodes, mỗi user được assign 2 random nodes:

8 workers: [W1, W2, W3, W4, W5, W6, W7, W8]

User Alice  → [W1, W3]
User Bob    → [W2, W7]
User Carol  → [W3, W6]
User Dave   → [W1, W5]
User Evan   → [W4, W8]

Math: Số combination với (8 choose 2) = 28 distinct shuffles. Nếu Alice bị “noisy neighbor” với attacker (Bob), only 1/28 = 3.5% of users overlap với attacker.

Generalized: Với N nodes, shard size K:

AWS Route 53: 2048 servers, mỗi customer dùng 4 servers → C(2048, 4) ≈ 7 × 10^11 distinct shuffles. Probability 2 customers share all 4 servers ~ 0%.

Tham chiếu: AWS Builders Library — Workload isolation using shuffle-shardinghttps://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/

2.15.5 Cellular Router

Router maps requests → cell. Có 3 cách:

PatternMô tảTrade-off
DNS-basedcell-1.example.com, cell-2.example.com. Client biết cell mình thuộcSimple, nhưng client phải biết
Smart routerApp tier router: route(user_id) → cell_idHidden từ client, nhưng router là SPOF/bottleneck
Stateless edge routerEdge function (Cloudflare Workers) compute cell_id = hash(user_id)Rất scalable, nhưng cần consistent hashing

Best practice: Stateless edge router với consistent hashing. Cell mapping table được cache ở edge.

2.15.6 Production examples

SystemCell sizeRouting
AWS DynamoDBPartitions (cells)Internal routing layer
AWS LambdaCell = isolated execution environmentSmart router
AWS Route 53Shuffle-sharded across 2048 serversAnycast + shuffle
SlackWorkspace cellsDNS + smart router
StripeAPI cells per regionEdge routing

2.15.7 Khi nào KHÔNG dùng Cell-based?

  • System nhỏ (< 100K users) — overhead lớn hơn benefit
  • Workload có strong cross-user query (e.g., social network feed) — cell boundary kill performance
  • Team size nhỏ — operational complexity của N cells overwhelm

Tham chiếu thêm:


3. Estimation — Network Overhead & Service Mesh Latency

3.1 Network Overhead: Monolith vs Microservices

Scenario: E-commerce checkout flow. Trong monolith, checkout là 1 function call chain. Trong microservices, cần gọi qua mạng.

Monolith (in-process):

Mỗi function call: ~100ns overhead (negligible).

Microservices (network calls):

Mỗi service call thêm:

  • Serialization/Deserialization: ~0.5ms (JSON) hoặc ~0.1ms (Protobuf)
  • Network round trip (same DC): ~0.5ms
  • TLS handshake (nếu mTLS, first connection): ~2ms (subsequent: ~0ms with connection pooling)
  • Load balancer hop: ~0.2ms

REST (JSON):

gRPC (Protobuf):

Checkout flow cần 4 service calls (sequential):

Overhead percentage:

Aha: Network overhead khoảng 6-11% cho sequential calls. Nhưng nếu Hieu parallelize calls (User + Inventory song song, rồi Payment), overhead giảm đáng kể.

Parallelized flow (User check + Inventory check chạy song song):

3.2 Service Mesh Sidecar Latency

Khi thêm service mesh (Istio/Linkerd), mỗi request đi qua 2 sidecar proxies (source + destination):

Istio (Envoy proxy):

Linkerd (linkerd2-proxy):

Cho checkout flow (4 hops, gRPC + Istio):

3.3 Bandwidth overhead: Microservices

Mỗi service call gửi HTTP headers, TLS overhead, serialization envelope:

Nếu hệ thống có 10,000 requests/s, mỗi request trung bình 5 internal service calls:

Nhận xét: 320 Mbps bandwidth overhead chỉ cho internal communication. Đây là lý do service mesh + gRPC (binary, smaller headers) quan trọng ở scale lớn.

3.4 Tổng hợp

MetricMonolithMicroservices (REST)Microservices (gRPC)gRPC + Service Mesh
Checkout latency60ms66.8ms63.6ms67.6ms
Overhead0%11.3%6.0%12.7%
Bandwidth/call0~800 bytes~400 bytes~500 bytes

4. Security — Bảo mật Microservices

4.1 Service-to-Service Authentication (mTLS)

Trong monolith, các function gọi nhau trong cùng process → trusted. Trong microservices, mọi network call đều có thể bị intercept, spoof, hoặc tamper.

mTLS (mutual TLS): Cả client và server đều verify certificate của nhau.

Service A (client)                           Service B (server)
     │                                              │
     │──── ClientHello ────────────────────────────►│
     │◄─── ServerHello + Server Certificate ────────│
     │◄─── CertificateRequest ─────────────────────│
     │──── Client Certificate ─────────────────────►│
     │                                              │
     │  (Both sides verify each other's cert)       │
     │                                              │
     │◄════ Encrypted channel established ════════►│

Không có mTLS: Bất kỳ ai có network access đều có thể giả dạng Service A gọi Service B. Có mTLS: Chỉ services có certificate hợp lệ (signed by trusted CA) mới giao tiếp được.

Trong Service Mesh: mTLS được enable tự động, transparent — application code không cần thay đổi.

# Istio PeerAuthentication — enforce mTLS cho toàn namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: ecommerce
spec:
  mtls:
    mode: STRICT   # Chỉ chấp nhận mTLS, reject plaintext

4.2 Zero Trust Networking

Mô hình truyền thống: “Castle and moat” — tin tưởng mọi thứ bên trong network perimeter. Zero Trust: “Never trust, always verify” — mọi request đều phải authenticate + authorize, bất kể từ đâu.

Nguyên tắc Zero Trust cho Microservices:

  1. Verify identity: Mỗi service có identity riêng (certificate, service account)
  2. Least privilege: Service A chỉ được gọi đúng endpoints cần thiết của Service B
  3. Encrypt everywhere: Mọi communication đều encrypted (mTLS)
  4. Verify explicitly: Token-based auth cho mỗi request, không cache trust decisions lâu
  5. Assume breach: Design để khi 1 service bị compromise, blast radius nhỏ nhất
# Istio AuthorizationPolicy — chỉ cho Order Service gọi Payment Service
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-service-policy
  namespace: ecommerce
spec:
  selector:
    matchLabels:
      app: payment-service
  action: ALLOW
  rules:
    - from:
        - source:
            principals: ["cluster.local/ns/ecommerce/sa/order-service"]
      to:
        - operation:
            methods: ["POST"]
            paths: ["/api/v1/payments", "/api/v1/refunds"]

4.3 Secrets Management (HashiCorp Vault)

Microservices cần nhiều secrets: database passwords, API keys, certificates, encryption keys. Không bao giờ hardcode secrets trong code hoặc environment variables.

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ Order Service │     │Payment Service│     │ User Service  │
│              │     │              │     │              │
│  vault-agent │     │  vault-agent │     │  vault-agent │
│  (sidecar)   │     │  (sidecar)   │     │  (sidecar)   │
└──────┬───────┘     └──────┬───────┘     └──────┬───────┘
       │                    │                    │
       │    Authenticate    │                    │
       │    (K8s SA Token)  │                    │
       └────────────────────┼────────────────────┘
                            │
                     ┌──────▼──────┐
                     │ HashiCorp   │
                     │   Vault     │
                     │             │
                     │ Secrets:    │
                     │ - DB creds  │
                     │ - API keys  │
                     │ - TLS certs │
                     └─────────────┘

Vault Features cho Microservices:

  • Dynamic secrets: Vault tạo DB credentials tạm thời, tự động rotate → nếu bị leak, secret tự hết hạn
  • PKI (Certificate Authority): Vault cấp phát certificates cho mTLS, tự động renew
  • Transit engine: Encryption as a service — services gửi data cho Vault encrypt, không giữ encryption keys
  • Audit logging: Mọi lần truy cập secret đều được ghi log
# Vault Agent Injector annotation cho Kubernetes Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "order-service"
        vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/order-db"
        vault.hashicorp.com/agent-inject-template-db-creds: |
          {{- with secret "database/creds/order-db" -}}
          DB_HOST=order-db.ecommerce.svc
          DB_USER={{ .Data.username }}
          DB_PASS={{ .Data.password }}
          {{- end -}}
    spec:
      serviceAccountName: order-service
      containers:
        - name: order-service
          image: myrepo/order-service:v1.2.3

4.4 Network Policies (Kubernetes)

Default Kubernetes: Mọi pod có thể giao tiếp với mọi pod khác → quá rộng. Network Policies giới hạn traffic.

# Chỉ cho phép Order Service gọi Payment Service trên port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-service-ingress
  namespace: ecommerce
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: order-service
        - podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - protocol: TCP
          port: 8080
# Deny all by default — bắt buộc khai báo explicit allow
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: ecommerce
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

4.5 API Gateway Security

API Gateway là tuyến phòng thủ đầu tiên:

Security FeatureMô tả
AuthenticationVerify JWT token trước khi forward request tới services
Rate LimitingChặn abuse, DDoS ở edge → Tuan-09-Rate-Limiter
IP WhitelistingChỉ cho phép known IPs cho admin endpoints
WAF (Web Application Firewall)Chặn SQL injection, XSS, common attacks
Request ValidationValidate schema trước khi forward (reject malformed requests)
CORSKiểm soát cross-origin requests
Bot DetectionFingerprinting, CAPTCHA challenges
Response SanitizationLoại bỏ internal headers, stack traces trước khi trả về client
# Kong API Gateway — security plugins
plugins:
  - name: jwt
    config:
      secret_is_base64: false
      claims_to_verify:
        - exp
 
  - name: rate-limiting
    config:
      minute: 100
      hour: 10000
      policy: redis
 
  - name: ip-restriction
    config:
      allow:
        - 10.0.0.0/8       # Internal
        - 203.0.113.0/24    # Office IP
 
  - name: bot-detection
    config:
      deny:
        - "Scrapy"
        - "curl"

5. DevOps — Vận hành Microservices

5.1 Kubernetes Basics cho Microservices

Kubernetes (K8s) là nền tảng orchestration chuẩn cho microservices:

┌──────────────────────────── Kubernetes Cluster ─────────────────────────────┐
│                                                                             │
│  ┌─────────── Namespace: ecommerce ───────────────────────────────────┐    │
│  │                                                                     │    │
│  │  ┌─ Deployment: order-service ─┐  ┌─ Deployment: payment-service ─┐│    │
│  │  │  ┌─Pod─┐ ┌─Pod─┐ ┌─Pod─┐   │  │  ┌─Pod─┐ ┌─Pod─┐            ││    │
│  │  │  │ App │ │ App │ │ App │   │  │  │ App │ │ App │            ││    │
│  │  │  │Envoy│ │Envoy│ │Envoy│   │  │  │Envoy│ │Envoy│            ││    │
│  │  │  └─────┘ └─────┘ └─────┘   │  │  └─────┘ └─────┘            ││    │
│  │  └────────────────────────────┘  └────────────────────────────────┘│    │
│  │                                                                     │    │
│  │  ┌─ Service: order-svc (ClusterIP) ─┐                              │    │
│  │  │  Selector: app=order-service      │                              │    │
│  │  │  Port: 8080                       │                              │    │
│  │  └──────────────────────────────────┘                              │    │
│  │                                                                     │    │
│  │  ┌─ Ingress Controller (nginx/traefik) ─┐                          │    │
│  │  │  /api/orders → order-svc:8080         │                          │    │
│  │  │  /api/payments → payment-svc:8080     │                          │    │
│  │  └──────────────────────────────────────┘                          │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                             │
│  ┌─ Namespace: monitoring ────────────┐                                    │
│  │  Prometheus, Grafana, Jaeger        │                                    │
│  └────────────────────────────────────┘                                    │
└─────────────────────────────────────────────────────────────────────────────┘

Core Kubernetes objects cho microservices:

ObjectVai tròVí dụ
PodĐơn vị nhỏ nhất, chứa 1+ containersApp container + sidecar
DeploymentQuản lý replicas, rolling updates3 replicas order-service
ServiceStable networking endpointClusterIP, LoadBalancer
IngressHTTP routing từ bên ngoài vàoPath-based routing
ConfigMapConfig không nhạy cảmFeature flags, URLs
SecretConfig nhạy cảm (mã hoá base64)DB passwords (nên dùng Vault)
HPAAuto-scaling dựa trên metricsScale khi CPU > 70%
PDBĐảm bảo availability khi maintenanceMin 2 pods available
NetworkPolicyFirewall giữa podsRestrict inter-service traffic
NamespaceLogical isolationecommerce, monitoring, staging

5.2 Helm Charts — Package Manager cho Kubernetes

Helm giúp template hoá và quản lý Kubernetes manifests:

microservices-chart/
├── Chart.yaml
├── values.yaml                    # Default values
├── values-staging.yaml            # Override cho staging
├── values-production.yaml         # Override cho production
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── hpa.yaml
    ├── ingress.yaml
    ├── networkpolicy.yaml
    └── _helpers.tpl
# values.yaml — shared config cho mọi microservice
replicaCount: 2
 
image:
  repository: myrepo/{{ .Chart.Name }}
  tag: "latest"
  pullPolicy: IfNotPresent
 
service:
  type: ClusterIP
  port: 8080
 
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi
 
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70
 
vault:
  enabled: true
  role: ""  # Override per service
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "chart.fullname" . }}
  labels:
    {{- include "chart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "chart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "chart.selectorLabels" . | nindent 8 }}
      annotations:
        {{- if .Values.vault.enabled }}
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: {{ .Values.vault.role | quote }}
        {{- end }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          livenessProbe:
            httpGet:
              path: /healthz
              port: {{ .Values.service.port }}
            initialDelaySeconds: 15
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /readyz
              port: {{ .Values.service.port }}
            initialDelaySeconds: 5
            periodSeconds: 5
# Deploy từng service
helm install order-service ./microservices-chart \
  --set image.repository=myrepo/order-service \
  --set image.tag=v1.2.3 \
  --set vault.role=order-service \
  -f values-production.yaml
 
helm install payment-service ./microservices-chart \
  --set image.repository=myrepo/payment-service \
  --set image.tag=v2.0.1 \
  --set vault.role=payment-service \
  -f values-production.yaml

5.3 Service Mesh Setup (Istio)

# Cài đặt Istio
istioctl install --set profile=demo -y
 
# Enable injection cho namespace
kubectl label namespace ecommerce istio-injection=enabled
 
# Verify sidecar injection
kubectl get pods -n ecommerce
# Mỗi pod sẽ có 2/2 containers (app + envoy sidecar)
# Istio VirtualService — Traffic management
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: order-service
  namespace: ecommerce
spec:
  hosts:
    - order-service
  http:
    - route:
        - destination:
            host: order-service
            subset: v1
          weight: 90
        - destination:
            host: order-service
            subset: v2
          weight: 10       # Canary: 10% traffic tới v2
      retries:
        attempts: 3
        perTryTimeout: 2s
        retryOn: gateway-error,connect-failure,refused-stream
      timeout: 10s
---
# DestinationRule — Circuit Breaker config
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: payment-service
  namespace: ecommerce
spec:
  host: payment-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: DEFAULT
        http1MaxPendingRequests: 50
        http2MaxRequests: 100
    outlierDetection:                    # Circuit Breaker
      consecutive5xxErrors: 5            # 5 lỗi liên tiếp
      interval: 10s                      # Trong 10 giây
      baseEjectionTime: 30s             # Eject 30 giây
      maxEjectionPercent: 50            # Tối đa eject 50% hosts
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

5.4 Distributed Tracing với Jaeger

# Jaeger deployment cho Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
    spec:
      containers:
        - name: jaeger
          image: jaegertracing/all-in-one:1.53
          ports:
            - containerPort: 16686   # Jaeger UI
            - containerPort: 14268   # Collector HTTP
            - containerPort: 4317    # OTLP gRPC
            - containerPort: 4318    # OTLP HTTP
          env:
            - name: COLLECTOR_OTLP_ENABLED
              value: "true"
# Istio telemetry — gửi traces tới Jaeger
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: mesh-default
  namespace: istio-system
spec:
  tracing:
    - providers:
        - name: jaeger
      randomSamplingPercentage: 10    # Sample 10% traffic (production)

5.5 Centralized Logging (EFK Stack)

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ Order Service │    │Payment Service│    │ User Service  │
│  (stdout/err) │    │  (stdout/err) │    │  (stdout/err) │
└──────┬───────┘    └──────┬───────┘    └──────┬───────┘
       │                    │                    │
       └────────────────────┼────────────────────┘
                            │
                    ┌───────▼────────┐
                    │   Fluent Bit    │  (DaemonSet, chạy trên mỗi node)
                    │  - Parse JSON   │
                    │  - Add metadata │
                    │    (pod, ns)    │
                    └───────┬────────┘
                            │
                    ┌───────▼────────┐
                    │ Elasticsearch   │  (hoặc OpenSearch, Loki)
                    │  - Index logs   │
                    │  - Full-text    │
                    │    search       │
                    └───────┬────────┘
                            │
                    ┌───────▼────────┐
                    │    Kibana       │  (hoặc Grafana)
                    │  - Dashboards   │
                    │  - Alerts       │
                    │  - Search       │
                    └────────────────┘

Structured logging là bắt buộc trong microservices:

{
  "timestamp": "2026-03-18T10:30:45.123Z",
  "level": "INFO",
  "service": "order-service",
  "traceId": "abc123def456",
  "spanId": "span789",
  "userId": "user-42",
  "orderId": "order-1001",
  "message": "Order created successfully",
  "duration_ms": 45,
  "metadata": {
    "items_count": 3,
    "total_amount": 150.00
  }
}

Quan trọng: traceId phải được propagate xuyên suốt mọi services để correlate logs với traces trong Jaeger.


6. Code Examples

6.1 Docker Compose — Multi-Service Setup với API Gateway

# docker-compose.yml
# E-commerce microservices with API Gateway
version: "3.8"
 
services:
  # ─── API Gateway (Kong) ───
  kong-database:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: kong
      POSTGRES_DB: kong
      POSTGRES_PASSWORD: kong_password
    volumes:
      - kong-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U kong"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  kong-migration:
    image: kong:3.5
    command: kong migrations bootstrap
    depends_on:
      kong-database:
        condition: service_healthy
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: kong_password
 
  api-gateway:
    image: kong:3.5
    depends_on:
      kong-migration:
        condition: service_completed_successfully
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: kong_password
      KONG_PROXY_LISTEN: 0.0.0.0:8000
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
      KONG_LOG_LEVEL: info
    ports:
      - "8000:8000"   # Proxy
      - "8001:8001"   # Admin API
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  # ─── User Service ───
  user-service:
    build: ./services/user-service
    environment:
      PORT: 3001
      DB_HOST: user-db
      DB_NAME: users
      NODE_ENV: production
    depends_on:
      user-db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
 
  user-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: users
      POSTGRES_USER: user_svc
      POSTGRES_PASSWORD: user_password
    volumes:
      - user-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user_svc"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  # ─── Order Service ───
  order-service:
    build: ./services/order-service
    environment:
      PORT: 3002
      DB_HOST: order-db
      DB_NAME: orders
      PAYMENT_SERVICE_URL: http://payment-service:3003
      USER_SERVICE_URL: http://user-service:3001
      RABBITMQ_URL: amqp://rabbitmq:5672
      NODE_ENV: production
    depends_on:
      order-db:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3002/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
 
  order-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: orders
      POSTGRES_USER: order_svc
      POSTGRES_PASSWORD: order_password
    volumes:
      - order-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U order_svc"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  # ─── Payment Service ───
  payment-service:
    build: ./services/payment-service
    environment:
      PORT: 3003
      DB_HOST: payment-db
      DB_NAME: payments
      RABBITMQ_URL: amqp://rabbitmq:5672
      NODE_ENV: production
    depends_on:
      payment-db:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3003/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
 
  payment-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: payments
      POSTGRES_USER: payment_svc
      POSTGRES_PASSWORD: payment_password
    volumes:
      - payment-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U payment_svc"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  # ─── Message Broker ───
  rabbitmq:
    image: rabbitmq:3.13-management-alpine
    ports:
      - "15672:15672"   # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: rabbit
      RABBITMQ_DEFAULT_PASS: rabbit_password
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_running"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  # ─── Observability ───
  jaeger:
    image: jaegertracing/all-in-one:1.53
    ports:
      - "16686:16686"   # Jaeger UI
      - "4318:4318"     # OTLP HTTP
    environment:
      COLLECTOR_OTLP_ENABLED: "true"
 
volumes:
  kong-db-data:
  user-db-data:
  order-db-data:
  payment-db-data:

6.2 Circuit Breaker in Node.js (Opossum)

// services/order-service/lib/circuit-breaker.js
const CircuitBreaker = require('opossum');
const axios = require('axios');
 
/**
 * Factory function tạo circuit breaker cho mỗi downstream service.
 * Mỗi service có breaker riêng → Bulkhead pattern.
 */
function createServiceBreaker(serviceName, baseURL, options = {}) {
  const defaultOptions = {
    timeout: 3000,             // 3s timeout cho mỗi request
    errorThresholdPercentage: 50, // Mở circuit khi 50% requests fail
    resetTimeout: 30000,       // Thử lại sau 30s (HALF-OPEN)
    rollingCountTimeout: 10000,// Window 10s để tính error %
    rollingCountBuckets: 10,   // 10 buckets x 1s
    volumeThreshold: 5,        // Cần ít nhất 5 requests trước khi tính %
    ...options,
  };
 
  // Function được wrap bởi circuit breaker
  async function makeRequest({ method = 'GET', path = '/', data = null, headers = {} }) {
    const response = await axios({
      method,
      url: `${baseURL}${path}`,
      data,
      headers: {
        'Content-Type': 'application/json',
        ...headers,
      },
      timeout: defaultOptions.timeout,
    });
    return response.data;
  }
 
  const breaker = new CircuitBreaker(makeRequest, defaultOptions);
 
  // ─── Event handlers for observability ───
  breaker.on('success', (result) => {
    console.log(JSON.stringify({
      level: 'DEBUG',
      service: serviceName,
      event: 'circuit_breaker_success',
      state: breaker.status.stats,
    }));
  });
 
  breaker.on('timeout', () => {
    console.log(JSON.stringify({
      level: 'WARN',
      service: serviceName,
      event: 'circuit_breaker_timeout',
      message: `Request to ${serviceName} timed out after ${defaultOptions.timeout}ms`,
    }));
  });
 
  breaker.on('reject', () => {
    console.log(JSON.stringify({
      level: 'WARN',
      service: serviceName,
      event: 'circuit_breaker_rejected',
      message: `Circuit is OPEN for ${serviceName}. Request rejected.`,
    }));
  });
 
  breaker.on('open', () => {
    console.log(JSON.stringify({
      level: 'ERROR',
      service: serviceName,
      event: 'circuit_breaker_opened',
      message: `Circuit OPENED for ${serviceName}! Too many failures.`,
    }));
  });
 
  breaker.on('halfOpen', () => {
    console.log(JSON.stringify({
      level: 'INFO',
      service: serviceName,
      event: 'circuit_breaker_half_open',
      message: `Circuit HALF-OPEN for ${serviceName}. Testing with probe request...`,
    }));
  });
 
  breaker.on('close', () => {
    console.log(JSON.stringify({
      level: 'INFO',
      service: serviceName,
      event: 'circuit_breaker_closed',
      message: `Circuit CLOSED for ${serviceName}. Service recovered.`,
    }));
  });
 
  // ─── Fallback: trả về response mặc định khi circuit OPEN ───
  breaker.fallback((request, error) => {
    console.log(JSON.stringify({
      level: 'WARN',
      service: serviceName,
      event: 'circuit_breaker_fallback',
      error: error.message,
      message: `Fallback activated for ${serviceName}`,
    }));
 
    return {
      fallback: true,
      service: serviceName,
      message: `${serviceName} is temporarily unavailable. Using fallback response.`,
      timestamp: new Date().toISOString(),
    };
  });
 
  return breaker;
}
 
// ─── Tạo breakers cho từng downstream service ───
const paymentBreaker = createServiceBreaker(
  'payment-service',
  process.env.PAYMENT_SERVICE_URL || 'http://payment-service:3003',
  { timeout: 5000, resetTimeout: 60000 } // Payment cần timeout dài hơn
);
 
const userBreaker = createServiceBreaker(
  'user-service',
  process.env.USER_SERVICE_URL || 'http://user-service:3001',
  { timeout: 2000 } // User service nên nhanh
);
 
// ─── Sử dụng ───
async function getUser(userId, traceId) {
  return userBreaker.fire({
    method: 'GET',
    path: `/api/users/${userId}`,
    headers: { 'X-Trace-Id': traceId },
  });
}
 
async function processPayment(paymentData, traceId) {
  return paymentBreaker.fire({
    method: 'POST',
    path: '/api/payments',
    data: paymentData,
    headers: { 'X-Trace-Id': traceId },
  });
}
 
// ─── Health check endpoint exposes circuit states ───
function getCircuitHealth() {
  return {
    payment: {
      state: paymentBreaker.opened ? 'OPEN' : paymentBreaker.halfOpen ? 'HALF-OPEN' : 'CLOSED',
      stats: paymentBreaker.status.stats,
    },
    user: {
      state: userBreaker.opened ? 'OPEN' : userBreaker.halfOpen ? 'HALF-OPEN' : 'CLOSED',
      stats: userBreaker.status.stats,
    },
  };
}
 
module.exports = { getUser, processPayment, getCircuitHealth };

6.3 Saga Pattern — Orchestration Example (Node.js)

// services/order-service/lib/saga-orchestrator.js
const amqp = require('amqplib');
const { processPayment } = require('./circuit-breaker');
 
/**
 * Saga Orchestrator cho Order Creation flow.
 *
 * Steps:
 *   1. Create Order (local DB)
 *   2. Reserve Inventory (async via queue)
 *   3. Process Payment (sync via circuit breaker)
 *   4. Confirm Order (local DB)
 *
 * Compensating Transactions:
 *   3 fails → Release Inventory → Cancel Order
 *   2 fails → Cancel Order
 */
 
// ─── Saga State Machine ───
const SAGA_STATES = {
  STARTED: 'STARTED',
  INVENTORY_RESERVED: 'INVENTORY_RESERVED',
  PAYMENT_PROCESSED: 'PAYMENT_PROCESSED',
  COMPLETED: 'COMPLETED',
  COMPENSATING: 'COMPENSATING',
  FAILED: 'FAILED',
};
 
class OrderSaga {
  constructor(orderData, { orderRepo, channel, logger }) {
    this.orderData = orderData;
    this.orderRepo = orderRepo;
    this.channel = channel;       // RabbitMQ channel
    this.logger = logger;
    this.sagaLog = [];             // Audit trail
    this.state = SAGA_STATES.STARTED;
    this.orderId = null;
    this.compensations = [];       // Stack of compensation functions
  }
 
  log(step, status, details = {}) {
    const entry = {
      timestamp: new Date().toISOString(),
      sagaId: this.orderId,
      step,
      status,
      state: this.state,
      ...details,
    };
    this.sagaLog.push(entry);
    this.logger.info(JSON.stringify(entry));
  }
 
  async execute() {
    try {
      // Step 1: Create Order (PENDING status)
      await this.step_createOrder();
 
      // Step 2: Reserve Inventory
      await this.step_reserveInventory();
 
      // Step 3: Process Payment
      await this.step_processPayment();
 
      // Step 4: Confirm Order
      await this.step_confirmOrder();
 
      this.state = SAGA_STATES.COMPLETED;
      this.log('saga', 'COMPLETED', { orderId: this.orderId });
 
      return { success: true, orderId: this.orderId, sagaLog: this.sagaLog };
 
    } catch (error) {
      this.log('saga', 'FAILED', { error: error.message });
      this.state = SAGA_STATES.COMPENSATING;
 
      // Execute compensating transactions in reverse order
      await this.compensate();
 
      return { success: false, orderId: this.orderId, error: error.message, sagaLog: this.sagaLog };
    }
  }
 
  // ─── Step 1: Create Order ───
  async step_createOrder() {
    this.log('create_order', 'EXECUTING');
 
    const order = await this.orderRepo.create({
      ...this.orderData,
      status: 'PENDING',
      createdAt: new Date(),
    });
    this.orderId = order.id;
 
    // Push compensation: cancel order
    this.compensations.push(async () => {
      this.log('create_order', 'COMPENSATING');
      await this.orderRepo.updateStatus(this.orderId, 'CANCELLED');
      this.log('create_order', 'COMPENSATED');
    });
 
    this.log('create_order', 'SUCCESS', { orderId: this.orderId });
  }
 
  // ─── Step 2: Reserve Inventory ───
  async step_reserveInventory() {
    this.log('reserve_inventory', 'EXECUTING');
 
    // Publish to inventory queue and wait for response
    const result = await this.publishAndWait(
      'inventory.reserve',
      {
        orderId: this.orderId,
        items: this.orderData.items,
      },
      'inventory.reserve.result',
      10000  // 10s timeout
    );
 
    if (!result.success) {
      throw new Error(`Inventory reservation failed: ${result.reason}`);
    }
 
    this.state = SAGA_STATES.INVENTORY_RESERVED;
 
    // Push compensation: release inventory
    this.compensations.push(async () => {
      this.log('reserve_inventory', 'COMPENSATING');
      await this.publishMessage('inventory.release', {
        orderId: this.orderId,
        items: this.orderData.items,
      });
      this.log('reserve_inventory', 'COMPENSATED');
    });
 
    this.log('reserve_inventory', 'SUCCESS', { reservationId: result.reservationId });
  }
 
  // ─── Step 3: Process Payment ───
  async step_processPayment() {
    this.log('process_payment', 'EXECUTING');
 
    // Sync call via circuit breaker
    const paymentResult = await processPayment(
      {
        orderId: this.orderId,
        amount: this.orderData.totalAmount,
        currency: this.orderData.currency || 'VND',
        userId: this.orderData.userId,
      },
      this.orderId  // traceId
    );
 
    if (paymentResult.fallback) {
      throw new Error('Payment service unavailable (circuit breaker fallback)');
    }
 
    if (!paymentResult.success) {
      throw new Error(`Payment failed: ${paymentResult.reason}`);
    }
 
    this.state = SAGA_STATES.PAYMENT_PROCESSED;
 
    // Push compensation: refund payment
    this.compensations.push(async () => {
      this.log('process_payment', 'COMPENSATING');
      try {
        await processPayment(
          {
            orderId: this.orderId,
            amount: this.orderData.totalAmount,
            type: 'REFUND',
            originalTransactionId: paymentResult.transactionId,
          },
          this.orderId
        );
      } catch (refundErr) {
        // Payment refund failure is critical — alert and manual intervention needed
        this.log('process_payment', 'COMPENSATION_FAILED', { error: refundErr.message });
        // TODO: Publish to dead-letter queue for manual processing
      }
      this.log('process_payment', 'COMPENSATED');
    });
 
    this.log('process_payment', 'SUCCESS', { transactionId: paymentResult.transactionId });
  }
 
  // ─── Step 4: Confirm Order ───
  async step_confirmOrder() {
    this.log('confirm_order', 'EXECUTING');
    await this.orderRepo.updateStatus(this.orderId, 'CONFIRMED');
 
    // Publish event for downstream services (notification, analytics)
    await this.publishMessage('order.confirmed', {
      orderId: this.orderId,
      userId: this.orderData.userId,
      totalAmount: this.orderData.totalAmount,
    });
 
    this.log('confirm_order', 'SUCCESS');
  }
 
  // ─── Compensate: reverse all completed steps ───
  async compensate() {
    this.log('compensation', 'STARTED', { stepsToCompensate: this.compensations.length });
 
    // Execute compensations in reverse order (LIFO)
    while (this.compensations.length > 0) {
      const compensationFn = this.compensations.pop();
      try {
        await compensationFn();
      } catch (compError) {
        this.log('compensation', 'STEP_FAILED', { error: compError.message });
        // Continue compensating remaining steps
      }
    }
 
    this.state = SAGA_STATES.FAILED;
    this.log('compensation', 'COMPLETED');
  }
 
  // ─── Helper: Publish message to RabbitMQ ───
  async publishMessage(queue, message) {
    await this.channel.assertQueue(queue, { durable: true });
    this.channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)), {
      persistent: true,
      messageId: `${this.orderId}-${Date.now()}`,
    });
  }
 
  // ─── Helper: Publish and wait for response (RPC pattern) ───
  async publishAndWait(requestQueue, message, responseQueue, timeoutMs) {
    return new Promise(async (resolve, reject) => {
      const correlationId = `${this.orderId}-${Date.now()}`;
      const timeout = setTimeout(() => {
        reject(new Error(`Timeout waiting for response from ${requestQueue}`));
      }, timeoutMs);
 
      // Setup response listener
      await this.channel.assertQueue(responseQueue, { durable: true });
      const { consumerTag } = await this.channel.consume(responseQueue, (msg) => {
        if (msg.properties.correlationId === correlationId) {
          clearTimeout(timeout);
          this.channel.cancel(consumerTag);
          this.channel.ack(msg);
          resolve(JSON.parse(msg.content.toString()));
        }
      });
 
      // Send request
      await this.channel.assertQueue(requestQueue, { durable: true });
      this.channel.sendToQueue(requestQueue, Buffer.from(JSON.stringify(message)), {
        persistent: true,
        correlationId,
        replyTo: responseQueue,
      });
    });
  }
}
 
// ─── Usage in Express route ───
async function createOrder(req, res) {
  const channel = req.app.get('rabbitChannel');
  const orderRepo = req.app.get('orderRepo');
  const logger = req.app.get('logger');
 
  const saga = new OrderSaga(req.body, { orderRepo, channel, logger });
  const result = await saga.execute();
 
  if (result.success) {
    res.status(201).json({
      orderId: result.orderId,
      status: 'CONFIRMED',
      message: 'Order created successfully',
    });
  } else {
    res.status(500).json({
      orderId: result.orderId,
      status: 'FAILED',
      error: result.error,
      message: 'Order creation failed. All changes have been rolled back.',
    });
  }
}
 
module.exports = { OrderSaga, createOrder };

7. Mermaid Diagrams

7.1 Microservices Architecture Overview

flowchart TD
    subgraph Clients
        WEB[Web App]
        MOB[Mobile App]
        IOT[IoT Device]
    end

    subgraph "Edge Layer"
        CDN[CDN / WAF]
        LB[Load Balancer]
    end

    subgraph "API Layer"
        GW[API Gateway<br/>Kong / Envoy]
        BFF_WEB[BFF Web]
        BFF_MOB[BFF Mobile]
    end

    subgraph "Service Mesh (Istio)"
        subgraph "Core Services"
            US[User Service<br/>+ Envoy Sidecar]
            OS[Order Service<br/>+ Envoy Sidecar]
            PS[Payment Service<br/>+ Envoy Sidecar]
            PRS[Product Service<br/>+ Envoy Sidecar]
            IS[Inventory Service<br/>+ Envoy Sidecar]
        end

        subgraph "Supporting Services"
            NS[Notification Service<br/>+ Envoy Sidecar]
            SS[Search Service<br/>+ Envoy Sidecar]
        end

        CP[Istiod<br/>Control Plane]
    end

    subgraph "Async Communication"
        MQ[Message Broker<br/>RabbitMQ / Kafka]
    end

    subgraph "Data Layer"
        UDB[(User DB<br/>PostgreSQL)]
        ODB[(Order DB<br/>PostgreSQL)]
        PDB[(Payment DB<br/>PostgreSQL)]
        PRDB[(Product DB<br/>MongoDB)]
        IDB[(Inventory DB<br/>Redis)]
        ES[(Elasticsearch)]
    end

    subgraph "Observability"
        PROM[Prometheus]
        GRAF[Grafana]
        JAEG[Jaeger]
        EFK[EFK Stack]
    end

    subgraph "Security"
        VAULT[HashiCorp Vault]
    end

    WEB --> CDN
    MOB --> CDN
    IOT --> CDN
    CDN --> LB
    LB --> GW
    GW --> BFF_WEB
    GW --> BFF_MOB
    BFF_WEB --> US & OS & PRS
    BFF_MOB --> US & OS & PRS
    OS -->|gRPC| PS
    OS -->|gRPC| IS
    OS -->|event| MQ
    PS -->|event| MQ
    MQ --> NS
    MQ --> SS
    US --- UDB
    OS --- ODB
    PS --- PDB
    PRS --- PRDB
    IS --- IDB
    SS --- ES
    CP -.->|config| US & OS & PS & PRS & IS & NS & SS
    VAULT -.->|secrets| US & OS & PS & PRS & IS
    US & OS & PS -.->|metrics| PROM
    PROM --> GRAF
    US & OS & PS -.->|traces| JAEG
    US & OS & PS -.->|logs| EFK

    style GW fill:#e65100,color:#fff,stroke:#333
    style CP fill:#1565c0,color:#fff,stroke:#333
    style VAULT fill:#7b1fa2,color:#fff,stroke:#333
    style MQ fill:#2e7d32,color:#fff,stroke:#333

7.2 Saga Flow — Order Creation (Orchestration)

sequenceDiagram
    participant C as Client
    participant GW as API Gateway
    participant O as Order Service<br/>(Saga Orchestrator)
    participant I as Inventory Service
    participant P as Payment Service
    participant N as Notification Service
    participant MQ as Message Queue

    C->>GW: POST /api/orders
    GW->>O: Forward request

    Note over O: Step 1: Create Order (PENDING)
    O->>O: INSERT order (status=PENDING)

    Note over O: Step 2: Reserve Inventory
    O->>MQ: Publish "inventory.reserve"
    MQ->>I: Consume
    I->>I: Reserve items
    I->>MQ: Publish "inventory.reserved" ✓
    MQ->>O: Consume result

    Note over O: Step 3: Process Payment
    O->>P: POST /api/payments (via Circuit Breaker)
    P->>P: Charge customer
    P->>O: 200 OK {transactionId}

    Note over O: Step 4: Confirm Order
    O->>O: UPDATE order (status=CONFIRMED)
    O->>MQ: Publish "order.confirmed"
    MQ->>N: Consume
    N->>N: Send email/SMS

    O->>GW: 201 Created
    GW->>C: 201 {orderId, status: CONFIRMED}

    Note over O,P: ═══ FAILURE SCENARIO ═══

    Note over O: If Payment fails at Step 3:
    P-->>O: 402 Payment Failed

    Note over O: Compensate Step 2: Release Inventory
    O->>MQ: Publish "inventory.release"
    MQ->>I: Consume
    I->>I: Release reserved items

    Note over O: Compensate Step 1: Cancel Order
    O->>O: UPDATE order (status=CANCELLED)

    O->>GW: 500 {error, status: FAILED}
    GW->>C: 500 Order creation failed

7.3 Circuit Breaker State Machine

stateDiagram-v2
    [*] --> CLOSED

    CLOSED --> OPEN : Failure count >= threshold<br/>(e.g., 5 failures in 10s)
    CLOSED --> CLOSED : Success / Failure < threshold

    OPEN --> HALF_OPEN : Reset timeout expires<br/>(e.g., after 30s)
    OPEN --> OPEN : All requests rejected<br/>(return fallback immediately)

    HALF_OPEN --> CLOSED : Probe request succeeds<br/>(service recovered!)
    HALF_OPEN --> OPEN : Probe request fails<br/>(still broken, wait more)

    note right of CLOSED
        Normal operation.
        Requests pass through.
        Count failures in rolling window.
    end note

    note right of OPEN
        Circuit tripped!
        All requests fail fast.
        Return fallback response.
        No load on failing service.
    end note

    note right of HALF_OPEN
        Testing recovery.
        Allow limited probe requests.
        If OK → close circuit.
        If fail → open again.
    end note

8. Aha Moments & Pitfalls

Aha Moments

#1 — Distributed Monolith là nightmare thực sự: Nếu deploy microservices nhưng các services share database, deploy phải đồng bộ, và thay đổi một service bắt buộc thay đổi service khác → em có một distributed monolith: tất cả nhược điểm của monolith + tất cả nhược điểm của distributed system, không có ưu điểm nào. Kiểm tra bằng câu hỏi: “Có thể deploy service A mà không cần deploy service B không?” Nếu không → distributed monolith.

#2 — “Monolith first” không phải yếu: Shopify chạy monolith Rails khổng lồ phục vụ hàng tỷ dollar GMV. Basecamp/37signals vẫn dùng monolith. Microservices là trade-off, không phải upgrade. Amazon, Netflix chuyển sang microservices khi team đã > 1000 engineers và codebase không thể manage được nữa.

#3 — Mỗi network call là một failure point mới: Trong monolith, function call gần như không bao giờ fail (trừ OOM). Trong microservices, mỗi network call có thể fail vì: network partition, DNS resolution failure, timeout, service crash, resource exhaustion. Với 10 services chained, nếu mỗi service có 99.9% availability:

99% = 87.6 giờ downtime/năm. Đó là lý do Circuit Breaker, Retry, Timeout, Fallback tồn tại.

#4 — Saga không replace ACID: Saga chỉ đảm bảo eventual consistency. Giữa step 2 và step 3 của saga, hệ thống ở trạng thái inconsistent (inventory reserved nhưng chưa payment). Cần thiết kế UI/UX cho trạng thái “processing” và handle edge cases (user refresh trang giữa saga, duplicate submit).

#5 — Observability là non-negotiable: Trong monolith, grep log file là đủ. Trong microservices, nếu không có distributed tracing + centralized logging + metrics, debugging production issue sẽ giống như tìm kim trong 10 đống rơm cùng lúc. Phải setup observability trước khi deploy microservices, không phải sau.

Pitfalls — Sai lầm thường gặp

Pitfall 1: Too Many Microservices Too Early (Nano-services)

Sai: Mới bắt đầu project, 3 developers, tách thành 15 services. Đúng: Bắt đầu với monolith modular (monolith với clear module boundaries). Khi 1 module cần scale/deploy riêng → tách ra thành service. “If you can’t build a well-structured monolith, what makes you think microservices is the answer?” — Simon Brown.

Pitfall 2: Shared Database Coupling

Sai: 3 services cùng query bảng users trong shared PostgreSQL. Đúng: User Service sở hữu users table. Các service khác gọi API User Service hoặc subscribe events để build local copy (CQRS). Đau lúc đầu nhưng tránh coupling nightmare về lâu dài.

Pitfall 3: Synchronous Chains (Death by a Thousand Cuts)

Sai: A → B → C → D → E (5 sync calls chained). D chết → A timeout. Đúng: Dùng async events khi có thể. Nếu phải sync, dùng Circuit Breaker + Timeout + Fallback. Tối đa 2-3 sync hops.

Pitfall 4: Không có Contract Testing

Sai: Service A depend on response format của Service B. B thay đổi response → A crash ở production. Đúng: Dùng Consumer-Driven Contract Testing (Pact). Consumer (A) viết contract mô tả response mong đợi. Provider (B) verify contract trước khi deploy. CI/CD chặn deploy nếu break contract.

Pitfall 5: Ignoring Data Consistency

Sai: Tách services nhưng vẫn expect strong consistency giữa chúng (coi distributed system như single database). Đúng: Accept eventual consistency. Design cho idempotency. Dùng Saga cho distributed transactions. Implement outbox pattern để đảm bảo “write to DB + publish event” là atomic.

Pitfall 6: No API Versioning Strategy

Sai: Thay đổi API contract → tất cả consumers cần deploy cùng lúc. Đúng: Version APIs từ đầu (/api/v1/, /api/v2/). Support backward compatibility. Dùng header-based versioning hoặc URI-based versioning. Deprecate cũ sau khi consumers đã migrate.

Pitfall 7: Logging Without Correlation

Sai: Mỗi service log riêng, không có traceId. Khi bug xảy ra, không biết request đi qua đâu. Đúng: Propagate traceId qua tất cả services (HTTP header X-Trace-Id hoặc OpenTelemetry context). Mọi log entry phải có traceId. Dùng Jaeger/Zipkin để visualize trace.

Pitfall 8: Sidecar mesh ở scale lớn

Sai: Cluster 2000 pods × Istio sidecar 80MB = 160GB RAM cho mesh. Mesh upgrade = 2000 pod restart. Đúng: Evaluate Istio Ambient hoặc Cilium cho cluster lớn. L4 mTLS thông qua DaemonSet (ztunnel) hoặc eBPF, opt-in L7 features. Tham chiếu section 2.14.

Pitfall 9: Single cell — blast radius lớn

Sai: Microservices nhưng 1 shared DB cho tất cả users. 1 bad query → toàn bộ user impact. Đúng: Cell-based architecture chia user thành N cells độc lập. AWS DynamoDB, Lambda đều dùng pattern này. Bad deployment trong 1 cell → chỉ 1/N user affected. Tham chiếu section 2.15.

Pitfall 10: Dual-write to DB + MQ

Sai: db.commit(); kafka.send(...) — nếu Kafka fail sau commit → data inconsistent. Đúng: Dùng Outbox Pattern. Đọc Tuan-Bonus-Outbox-Pattern cho full deep dive.

Pitfall 11: Saga compensation không idempotent

Sai: Compensation gọi 2 lần (vì retry) → refund 2 lần. Đúng: Mọi compensation action phải idempotent. Dùng UPDATE ... WHERE status='charged' thay vì blindly refund.


Prerequisite (cần đọc trước)

Bonus chapters (Architect-level, advanced)

Cùng module (Architecture-DevOps-Security)

Áp dụng trong Case Studies

Liên quan khác


Tham khảo

  • Alex Xu, System Design Interview — Chapter 2 (Estimation), Chapter 4-13 (Patterns applied)
  • Sam Newman, Building Microservices (2nd Edition) — The definitive guide
  • Chris Richardson, Microservices Patterns — Saga, CQRS, Event Sourcing
  • Martin Fowler, Microservices Guidehttps://martinfowler.com/microservices/
  • Eric Evans, Domain-Driven Design — Bounded Context, Context Mapping
  • Microsoft, Cloud Design Patterns — Circuit Breaker, Bulkhead, Strangler Fig
  • Istio Documentation — https://istio.io/latest/docs/
  • HashiCorp Vault — https://developer.hashicorp.com/vault
  • sdi.anhvy.dev — Vietnamese System Design Reference

Tuần tới: Tuan-12-CICD-Pipeline — CI/CD Pipeline cho Microservices: mỗi service một pipeline, automated testing, canary deployment