Microservices Architecture: Khi Nào Nên Dùng, Khi Nào Nên Tránh
Martin Fowler cảnh báo: "Don't start with microservices. Start with a monolith, then extract services when you understand your domain boundaries." Sam Newman (tác giả "Building Microservices") nói thẳng: "Microservices are not a goal, they're a tool." Nhưng nhiều startup Việt Nam bắt đầu bằng microservices vì "Netflix dùng" — rồi chết vì complexity trước khi product-market fit. Netflix có 2,000+ engineers, bạn có 5. Bài viết này giúp bạn quyết định khách quan: monolith, modular monolith, hay microservices — đâu là lựa chọn đúng cho giai đoạn hiện tại, dựa trên team size, domain maturity, và operational capability.
Monolith vs Microservices: Trade-offs Thực Tế
🏢 Monolith
Ưu: Simple deployment (1 artifact), easy debugging (1 process, 1 log stream), no network latency giữa modules, 1 database = strong consistency, onboarding developer mới nhanh. Nhược: Scale toàn bộ (dù chỉ 1 module nặng), deploy toàn bộ khi thay 1 dòng code, team lớn → merge conflicts, technology lock-in. Phù hợp: Startup, team < 10 devs, product chưa rõ domain boundaries.
🔧 Microservices
Ưu: Scale từng service độc lập, deploy riêng lẻ (không ảnh hưởng services khác), team autonomy (mỗi team own 1-2 services), polyglot (mỗi service dùng tech phù hợp). Nhược: Network complexity, distributed transactions (saga pattern), eventual consistency, monitoring phức tạp (distributed tracing), N services = N CI/CD pipelines + N databases. Phù hợp: Team > 30 devs, clear domain boundaries, cần scale heterogeneous workloads.
Modular Monolith: The Sweet Spot
BanhCuonFlow dùng modular monolith — lựa chọn được Shopify (với hệ thống xử lý $500+ tỷ/năm) validate. 1 codebase, 1 deployment unit, nhưng code tổ chức thành modules rõ ràng: Task, Chat, Workflow, Report, Admin, Auth. Mỗi module có riêng: controllers, services, repositories, DTOs. Modules communicate qua well-defined interfaces và events, không reference trực tiếp internal classes. Lợi ích: Simple like monolith (1 deploy, 1 DB, easy debugging) + organized like microservices (clear boundaries, team can work independently on modules). Khi 1 module thực sự cần scale riêng — extract nó thành service riêng mà không cần rewrite toàn bộ.
Patterns Quan Trọng Khi Dùng Microservices
🚪 API Gateway
Single entry point cho tất cả clients (web, mobile, third-party). Routes requests đến đúng service dựa trên URL path hoặc header. Centralize cross-cutting concerns: authentication, rate limiting, SSL termination, request/response transformation, circuit breaker. Tools phổ biến: Kong (open-source, Lua plugins), YARP (.NET reverse proxy), AWS API Gateway (serverless), Envoy (cloud-native, dùng trong Istio service mesh).
📡 Service Communication
Synchronous (HTTP/gRPC): Service A gọi Service B, chờ response. Simple, familiar, request-reply pattern. Nhưng: coupling cao, cascading failures (B down → A cũng down), latency tăng theo call chain depth. Asynchronous (Message Queue): Service A publish event vào RabbitMQ/Kafka, Service B consume khi ready. Decoupled (A không biết B tồn tại), resilient (B down → messages queue, xử lý khi B recovery), nhưng eventual consistency — data mất vài giây mới sync. Best practice 2025: sync cho queries (cần response ngay), async cho commands (fire-and-forget, side effects).
💃 Saga Pattern
Distributed transaction across services — thay thế 2-phase commit (2PC) quá chậm và brittle. Ví dụ: Tạo order → Trừ inventory → Charge payment. Nếu payment fail → compensate: restore inventory, cancel order. 2 loại: Choreography (mỗi service listen events và react — đơn giản cho 2-3 services) vs Orchestration (1 central coordinator điều phối flow — tốt cho complex flows 5+ services). BanhCuonFlow dùng choreography cho internal events (task created → notify → update dashboard).
5 Pitfalls Microservices Thực Tế
① Distributed monolith: Tách services nhưng share database, deploy cùng lúc, change 1 service → phải change 3 services khác. Kết quả: complexity của microservices + costs của monolith. Fix: mỗi service own database riêng, loose coupling qua events.
② Nano-services: Tách quá nhỏ — 50 services cho 1 CRUD app. Mỗi service 100 lines of code. Network overhead > business logic. Operational cost astronomical (50 deployments, 50 monitoring dashboards). Fix: merge related services, follow domain boundaries không phải entity boundaries.
③ Ignore observability: 20 services mà không distributed tracing → "request chậm ở service nào?" Debug mất 3 ngày. Fix: implement OpenTelemetry traces, Jaeger/Zipkin visualization, centralized logging (ELK/Loki).
④ Shared libraries everywhere: Common library update → re-deploy tất cả services. Tạo coupling ngầm. Fix: duplicate code nhỏ ok, chỉ share truly cross-cutting (logging, auth).
⑤ Team structure mismatch: Conway's Law — hệ thống architecture mirror tổ chức. 1 team maintain 10 services = bottleneck. Fix: team topology — mỗi team own 1-2 services end-to-end (build, deploy, operate).