Chương 8. Microservices Thật Ra Giải Quyết Vấn Đề Gì?
Microservices là một trong những từ dễ gây hiểu lầm nhất trong kiến trúc hệ thống.
Nhiều người nghĩ:
- Microservices nghĩa là hệ thống hiện đại.
- Microservices tự động scale tốt hơn.
- Microservices tốt hơn monolith.
- Công ty lớn dùng microservices nên startup cũng nên dùng.
- Cứ tách mỗi module thành một service là xong.
Những suy nghĩ này nguy hiểm.
Microservices không phải phần thưởng khi hệ thống "lên trình". Nó là một cách tổ chức hệ thống để giải quyết một số loại áp lực rất cụ thể. Nếu áp lực đó chưa tồn tại, microservices có thể làm hệ thống chậm hơn, khó debug hơn, khó deploy hơn và tốn người vận hành hơn.
Chương này trả lời câu hỏi quan trọng:
> Microservices thật ra giải quyết vấn đề gì?
---
8.1. Ví dụ quán bánh mở thành chuỗi
Ở Chương 7, quán bánh đã chia thành nhiều bộ phận trong cùng một cửa hàng:
- Nhận đơn.
- Bếp.
- Giao hàng.
- Thu tiền.
- Chăm sóc khách.
- Kế toán.
Đó giống modular monolith: vẫn một cửa hàng, nhưng có bộ phận rõ.
Bây giờ quán phát triển mạnh. Nó mở nhiều chi nhánh, bán online toàn quốc, có bếp trung tâm, có đội giao hàng riêng, có bộ phận kế toán riêng, có kho nguyên liệu riêng, có hệ thống chăm sóc khách riêng.
Lúc này, một cửa hàng duy nhất không còn đủ.
Mỗi bộ phận có thể cần vận hành như một đơn vị riêng:
- Kho nguyên liệu có hệ thống riêng.
- Bếp trung tâm có hệ thống riêng.
- Giao hàng có hệ thống riêng.
- Kế toán có hệ thống riêng.
- App đặt hàng có hệ thống riêng.
Các đơn vị này phải nói chuyện với nhau qua hợp đồng rõ ràng:
- Đơn hàng mới được tạo.
- Kho xác nhận còn nguyên liệu.
- Bếp nhận phiếu làm bánh.
- Giao hàng nhận yêu cầu giao.
- Kế toán ghi nhận giao dịch.
Đây giống microservices hơn.
Không phải vì chia ra nghe sang hơn, mà vì quy mô và cách vận hành đã thay đổi.
---
8.2. Microservices là gì?
Microservices là cách chia hệ thống thành nhiều service nhỏ hơn, mỗi service:
- Sở hữu một vùng nghiệp vụ rõ ràng.
- Có thể deploy độc lập.
- Có thể scale độc lập.
- Có thể fail tương đối độc lập.
- Thường sở hữu dữ liệu riêng.
- Giao tiếp với service khác qua API/message.
Ví dụ:
Frontend
|
API Gateway
|-- User Service
|-- Catalog Service
|-- Order Service
|-- Payment Service
|-- Notification Service
Điểm khác biệt với modular monolith:
Modular monolith:
module khác nhau, nhưng cùng app, cùng deploy
Microservices:
service khác nhau, chạy riêng, deploy riêng, giao tiếp qua network
Microservices không chỉ là chia folder. Nó là tách runtime, deploy, network, ownership và thường cả database.
---
8.3. Vấn đề 1: cần deploy độc lập
Trong monolith, thay đổi nhỏ ở notification vẫn phải deploy cả app.
Nếu team deploy tốt, chuyện này không sao. Nhưng khi hệ thống lớn:
- Team payment muốn deploy hotfix riêng.
- Team search muốn deploy thuật toán ranking mới mỗi ngày.
- Team notification muốn thay provider email.
- Team order không muốn bị ảnh hưởng bởi deploy của team khác.
Microservices cho phép:
Payment Service deploy riêng
Search Service deploy riêng
Notification Service deploy riêng
Điều này hữu ích khi:
- Nhiều team cùng phát triển.
- Mỗi phần có tốc độ thay đổi khác nhau.
- Một phần cần release nhanh mà không kéo toàn hệ thống.
Nhưng deploy độc lập chỉ có ý nghĩa nếu service thật sự độc lập.
Nếu mỗi lần deploy Payment vẫn phải deploy Order, User, Notification cùng lúc, thì bạn chưa có microservices thật. Bạn chỉ có distributed deployment phức tạp hơn.
---
8.4. Vấn đề 2: cần scale độc lập
Không phải phần nào của hệ thống cũng có cùng tải.
Ví dụ:
- Trang catalog được đọc rất nhiều.
- Payment ít request hơn nhưng cần cực kỳ đúng.
- Notification có thể bùng lên khi gửi campaign.
- Video processing cần CPU/GPU.
- Chat realtime cần nhiều connection.
- AI grading cần concurrency cao để chờ model provider.
Nếu tất cả nằm trong một monolith, đôi khi phải scale cả app chỉ vì một phần nhỏ.
Ví dụ:
Monolith gồm:
API thường
AI grading
Notification
Report
AI grading cần 100 worker/concurrency, nhưng API thường không cần. Nếu scale cả monolith theo AI grading, ta lãng phí tài nguyên.
Microservices hoặc tách worker/service riêng giúp:
API Service scale 3 instance
AI Service scale 20 instance
Notification Worker scale 10 instance
Report Worker chạy theo lịch
Nhưng cần nhớ: nhiều vấn đề scale có thể giải quyết bằng queue/worker riêng trước khi tách microservice hoàn chỉnh.
---
8.5. Vấn đề 3: cần failure isolation
Failure isolation nghĩa là một phần lỗi không làm chết toàn hệ thống.
Ví dụ:
- Email provider lỗi không được làm checkout chết.
- Report nặng không được làm API chính chậm.
- Search lỗi thì vẫn có thể mở trang sản phẩm cơ bản.
- Notification service chết thì đơn hàng vẫn tạo được.
- AI service quá tải thì hệ thống học tập chính vẫn hoạt động.
Trong monolith rối, một phần lỗi có thể kéo cả app xuống:
- Memory leak trong job làm web request chết.
- CPU-bound report chiếm hết CPU.
- External API không timeout giữ hết worker.
- Một module lỗi làm deploy toàn app rollback.
Microservices có thể giúp cách ly:
Notification Service chết -> Order Service vẫn sống
Search Service chậm -> Catalog có fallback
AI Service quá tải -> Main App vẫn nhận bài và báo pending
Nhưng failure isolation không tự có chỉ vì tách service.
Cần thêm:
- Timeout.
- Retry có kiểm soát.
- Circuit breaker.
- Fallback.
- Queue.
- Bulkhead.
- Monitoring.
Nếu service A gọi service B và chờ mãi không timeout, service B chết vẫn kéo service A chết theo.
---
8.6. Vấn đề 4: cần ownership rõ
Khi team nhỏ, mọi người cùng sửa một codebase có thể ổn.
Khi tổ chức lớn hơn, cần ownership:
- Team Payment sở hữu thanh toán.
- Team Search sở hữu tìm kiếm.
- Team Identity sở hữu user/auth.
- Team Notification sở hữu email/SMS/push.
- Team Data sở hữu pipeline analytics.
Microservices giúp vẽ ranh giới trách nhiệm.
Mỗi team có thể:
- Sở hữu code.
- Sở hữu database.
- Sở hữu API contract.
- Sở hữu deploy pipeline.
- Sở hữu dashboard/alert.
- Sở hữu reliability của service.
Đây là lý do quan trọng nhưng hay bị bỏ qua: microservices giải quyết vấn đề tổ chức con người nhiều không kém vấn đề kỹ thuật.
Nếu chỉ có một team nhỏ, ownership riêng chưa phải áp lực lớn. Khi đó modular monolith thường hiệu quả hơn.
---
8.7. Vấn đề 5: cần boundary bảo mật hoặc dữ liệu
Một số phần cần cách ly mạnh hơn.
Ví dụ:
- Payment chứa dữ liệu nhạy cảm.
- Identity/auth chứa credential và token.
- Medical/financial data cần kiểm soát truy cập nghiêm ngặt.
- Tenant enterprise cần isolation cao.
- Secret hoặc API key quan trọng không nên nằm trong mọi service.
Tách service có thể giúp giới hạn phạm vi truy cập:
- Chỉ Payment Service được nói chuyện với payment provider.
- Chỉ Identity Service quản lý password/token.
- Service khác không được query trực tiếp dữ liệu nhạy cảm.
Nhưng tách service không thay thế security tốt.
Vẫn cần:
- Authentication giữa services.
- Authorization.
- Secret management.
- Audit log.
- Network boundary.
- Encryption.
- Least privilege.
Nếu tách service nhưng mọi service vẫn dùng chung database user/password, security boundary không thật.
---
8.8. Cái giá 1: network thay cho function call
Trong monolith, module gọi nhau bằng hàm:
payment.create_payment(order_id)
Trong microservices, có thể phải gọi qua network:
POST /payments
Function call gần như tức thì và ít lỗi. Network call thì khác:
- Có latency.
- Có timeout.
- Có thể fail.
- Có thể mất kết nối.
- Có thể trả lỗi 500.
- Có thể version mismatch.
- Có thể bị rate limit.
Vì vậy, tách service biến một số lỗi đơn giản thành lỗi phân tán.
Trong monolith, nếu hàm payment lỗi, stack trace thường rõ. Trong microservices, request có thể đi qua 5 service, lỗi ở service thứ 4, log nằm ở nơi khác.
Nếu không có tracing/log correlation, debug sẽ rất mệt.
---
8.9. Cái giá 2: dữ liệu phân tán
Trong monolith, nhiều nghiệp vụ dùng chung database.
Ví dụ đặt hàng:
- Tạo order.
- Trừ tồn kho.
- Tạo payment.
- Ghi log.
Có thể nằm trong một transaction.
Trong microservices:
- Order Service có database riêng.
- Inventory Service có database riêng.
- Payment Service có database riêng.
Không thể đơn giản dùng một transaction.atomic bao trùm tất cả.
Ta phải đối mặt với:
- Eventual consistency.
- Saga.
- Compensating transaction.
- Duplicate event.
- Out-of-order event.
- Data duplication.
- Query tổng hợp khó hơn.
Ví dụ:
Order đã tạo, payment thành công, nhưng inventory update lỗi. Hệ thống xử lý thế nào?
Trong monolith, rollback có thể đơn giản hơn. Trong microservices, phải thiết kế workflow và bù trừ.
Dữ liệu phân tán là một trong những cái giá lớn nhất của microservices.
---
8.10. Cái giá 3: observability bắt buộc nghiêm túc hơn
Một monolith nhỏ có thể debug bằng log file tương đối đơn giản.
Microservices cần nhiều hơn:
- Centralized logs.
- Metrics từng service.
- Distributed tracing.
- Correlation ID.
- Alert theo service.
- Dashboard dependency.
- Error budget/SLO nếu hệ thống lớn.
Nếu không, khi user báo lỗi, ta không biết request đã đi qua đâu:
API Gateway
-> Order Service
-> Payment Service
-> Fraud Service
-> Notification Service
Lỗi có thể ở bất kỳ bước nào.
Microservices mà thiếu observability giống như chia căn nhà thành 20 phòng nhưng tắt hết đèn.
---
8.11. Cái giá 4: testing và contract phức tạp hơn
Trong monolith, test một use case có thể chạy trong cùng process.
Trong microservices, service phụ thuộc API của nhau.
Cần quan tâm:
- API contract.
- Backward compatibility.
- Consumer-driven contract testing.
- Versioning.
- Mock/fake service.
- Integration test.
- End-to-end test.
Ví dụ Payment Service đổi response:
{
"status": "paid"
}
thành:
{
"payment_status": "succeeded"
}
Nếu Order Service chưa cập nhật, hệ thống lỗi.
Microservices yêu cầu kỷ luật contract. Không thể đổi API tùy hứng.
---
8.12. Cái giá 5: vận hành nhiều hơn
Mỗi service cần:
- Build.
- Deploy.
- Config.
- Secret.
- Logs.
- Metrics.
- Alerts.
- Scaling.
- Health check.
- Rollback.
- Security.
10 service nghĩa là 10 thứ cần vận hành, không chỉ 10 folder code.
Nếu team chưa đủ năng lực vận hành, microservices có thể làm chậm team nghiêm trọng.
Các công ty lớn dùng microservices thường có:
- Platform team.
- DevOps/SRE.
- CI/CD tốt.
- Observability tốt.
- Cloud infrastructure tốt.
- Quy trình incident.
Nếu chưa có những thứ đó, hãy rất cẩn thận.
---
8.13. Microservices khác modular monolith thế nào?
| Tiêu chí | Modular Monolith | Microservices | |---|---|---| | Deploy | Chung | Riêng từng service | | Runtime | Cùng process/app | Nhiều process/service | | Giao tiếp | Gọi hàm/interface nội bộ | HTTP/gRPC/message qua network | | Database | Thường chung | Thường mỗi service sở hữu dữ liệu | | Transaction | Dễ hơn | Khó hơn | | Debug | Dễ hơn | Cần tracing/log tập trung | | Scale | Scale cả app hoặc worker riêng | Scale từng service | | Vận hành | Đơn giản hơn | Phức tạp hơn | | Phù hợp | Team nhỏ-vừa, domain đổi nhiều | Team lớn hơn, boundary rõ, scale/failure/deploy độc lập |
Modular monolith là bước rất tốt trước microservices vì nó giúp tìm ranh giới đúng mà chưa trả giá network/distributed data quá sớm.
---
8.14. Khi nào microservices đáng dùng?
Microservices đáng cân nhắc khi có nhiều điều kiện sau:
Có boundary nghiệp vụ rõ
Ví dụ:
- Payment
- Identity
- Search
- Notification
- Media processing
- Recommendation
Có nhu cầu deploy độc lập
Một phần thay đổi thường xuyên hoặc cần release riêng.
Có nhu cầu scale độc lập
Một phần có workload khác hẳn.
Có nhu cầu failure isolation
Một phần lỗi không được làm chết toàn hệ thống.
Có team/owner riêng
Service có người chịu trách nhiệm end-to-end.
Có khả năng vận hành
Ít nhất phải có:
- CI/CD.
- Logs/metrics/traces.
- Alerting.
- API contract.
- Monitoring database/queue.
- Quy trình rollback.
Nếu thiếu hầu hết các điều kiện này, microservices có thể là quá sớm.
---
8.15. Khi nào microservices là tự làm khổ mình?
Microservices thường là sai thời điểm khi:
- Team chỉ có vài người.
- Domain còn chưa rõ.
- Sản phẩm còn đổi liên tục.
- Chưa có người dùng/tải thật.
- Chưa có observability.
- Chưa có CI/CD ổn.
- Chưa có test/contract.
- Không có ai vận hành hạ tầng.
- Tách chỉ vì nghe hiện đại.
Khi đó, microservices tạo ra nhiều vấn đề hơn nó giải quyết:
- Chậm phát triển.
- Khó debug.
- Transaction khó.
- Dữ liệu phân tán.
- Deploy phức tạp.
- Chi phí cloud tăng.
- Mỗi thay đổi phải sửa nhiều service.
Đây là distributed monolith: hệ thống vẫn dính chặt, nhưng bị phân tán qua network.
---
8.16. Microservices không nhất thiết là bước đầu tiên để tách
Nếu một phần của monolith gây đau, có nhiều bước nhẹ hơn trước khi tách service hoàn chỉnh.
Tách module
Tổ chức code rõ hơn.
Tách queue
Job nặng có queue riêng.
Tách worker
Worker riêng xử lý workload riêng.
Tách database read model
Search index, cache, read replica, warehouse.
Tách API boundary
Định nghĩa interface rõ trước khi tách runtime.
Tách service
Chỉ khi các bước trên chưa đủ hoặc đã có lý do rõ.
Tư duy:
> Không phải cứ đau là cắt service. Có nhiều mức tách khác nhau.
---
8.17. Ví dụ: tách notification
Ban đầu trong monolith:
Order created -> gửi email ngay trong request
Vấn đề:
- Request đặt hàng chậm.
- Email provider lỗi làm đặt hàng lỗi.
Bước 1: Đưa vào queue.
Order created -> push SendEmail job -> worker gửi email
Monolith vẫn còn, nhưng request nhanh hơn và email lỗi không làm đặt hàng chết.
Bước 2: Tách module notification.
notification/
send_email
send_sms
templates
Logic email không còn rải khắp app.
Bước 3: Nếu notification rất lớn, cần deploy/scale riêng, tách service.
Order Service -> event OrderCreated -> Notification Service
Như vậy tách service là bước cuối của quá trình tiến hóa, không phải bước đầu tiên.
---
8.18. Ví dụ: tách AI processing
Một hệ thống có job AI mất 90 giây.
Sai lầm:
Web request -> gọi AI -> chờ 90 giây -> trả response
Bước tốt hơn:
Web request -> tạo job -> queue -> worker gọi AI -> lưu kết quả
Nếu worker trong monolith vẫn đủ, chưa cần service riêng.
Khi tải tăng:
- AI job cần concurrency cao.
- AI provider có rate limit riêng.
- AI worker cần autoscale riêng.
- AI lỗi không được ảnh hưởng web app.
- Team AI muốn deploy riêng.
Lúc đó có thể tách:
Main App -> Queue -> AI Service / AI Workers
Service AI có thể dùng runtime phù hợp hơn, scale riêng, monitoring riêng.
Điểm chính:
> Vấn đề ban đầu không phải "cần microservices", mà là "không được để web request chờ job AI dài". Queue/worker là bước đầu. Service riêng là bước sau nếu có áp lực.
---
8.19. Microservices và message queue
Message queue rất quan trọng trong nhiều hệ microservices, nhưng không phải mọi giao tiếp đều qua queue.
Có hai kiểu giao tiếp chính:
Gọi trực tiếp
Service A -> HTTP/gRPC -> Service B
Phù hợp khi:
- Cần kết quả ngay.
- Request ngắn.
- Failure được xử lý rõ.
Gửi message/event
Service A -> Broker -> Service B
Phù hợp khi:
- Việc lâu.
- Có thể xử lý sau.
- Cần retry.
- Cần nhiều consumer.
- Không muốn service A chờ service B.
Ví dụ:
- Order gọi Payment có thể cần đồng bộ tùy flow.
- Order phát event
OrderCreatedcho Notification có thể async. - Payment webhook xử lý xong phát event
PaymentSucceeded. - Analytics nghe nhiều event để ghi data pipeline.
Tư duy:
> Cần ngay thì API. Làm sau thì queue. Nhiều bên cần biết thì event. Cần replay thì event streaming.
---
8.20. Những lỗi tư duy phổ biến về microservices
Lỗi 1: Tách theo bảng
Mỗi bảng một service là công thức tạo thảm họa.
Service nên tách theo capability/domain, không tách máy móc theo table.
Lỗi 2: Shared database giữa nhiều service
Nếu nhiều service cùng đọc/ghi trực tiếp một database chung lung tung, boundary không thật.
Lỗi 3: Service quá nhỏ
Service quá nhỏ khiến hệ thống chatty, gọi nhau liên tục, debug khó.
Lỗi 4: Không có timeout
Service gọi nhau mà không timeout sẽ chết dây chuyền.
Lỗi 5: Retry vô tội vạ
Retry không backoff có thể tạo retry storm.
Lỗi 6: Không có observability
Không tracing/log correlation thì microservices rất khó debug.
Lỗi 7: Dùng event cho mọi thứ
Event tốt nhưng lạm dụng làm flow khó hiểu, eventual consistency khắp nơi.
Lỗi 8: Tách khi domain chưa rõ
Boundary sai sẽ làm thay đổi nghiệp vụ rất đau.
---
8.21. Checklist trước khi chọn microservices
Trước khi tách một service, hỏi:
- Service này có domain/capability rõ không?
- Nó có cần deploy riêng không?
- Nó có cần scale riêng không?
- Nó có failure mode riêng không?
- Nó có dữ liệu sở hữu rõ không?
- Nếu tách, service khác giao tiếp với nó qua gì?
- Giao tiếp cần sync hay async?
- Transaction hiện tại có bị phá không?
- Có cần saga/compensation không?
- Có monitoring/log/tracing chưa?
- Có test contract chưa?
- Ai chịu trách nhiệm vận hành service này?
- Nếu service chết, hệ thống degrade thế nào?
Nếu không trả lời được phần lớn câu hỏi, có lẽ chưa nên tách.
---
8.22. Kết luận của chương
Microservices không phải kiến trúc "cao cấp hơn" monolith. Nó là một lựa chọn có điều kiện.
Microservices giải quyết tốt các vấn đề:
- Deploy độc lập.
- Scale độc lập.
- Failure isolation.
- Ownership rõ.
- Boundary bảo mật/dữ liệu.
Nhưng nó mang lại cái giá:
- Network call.
- Dữ liệu phân tán.
- Distributed transaction.
- Observability phức tạp.
- Testing/contract khó hơn.
- Vận hành nhiều hơn.
Vì vậy, con đường thực dụng thường là:
Monolith rõ ràng
-> Modular monolith
-> Tách queue/worker/read model
-> Tách service khi có áp lực thật
Thông điệp quan trọng:
> Microservices không làm hệ thống tự nhiên tốt hơn. Nó chỉ đáng dùng khi vấn đề bạn gặp đúng là vấn đề microservices sinh ra để giải quyết, và bạn đủ năng lực trả cái giá đi kèm.