Tóm tắt: Microservices là mô hình tổ chức và vận hành, không phải mục tiêu bắt buộc của một hệ thống lớn.
Thuật ngữ chính: Modular Monolith: một ứng dụng nhưng được chia thành mô-đun rõ ràng; service boundary: ranh giới trách nhiệm của dịch vụ; strangler: thay thế hệ thống cũ từng phần.
Bài toán
Tách theo lớp kỹ thuật thường khiến mỗi yêu cầu phải gọi qua nhiều dịch vụ. Đội ngũ vẫn phải phối hợp khi phát hành, nhưng lại gánh thêm lỗi mạng và giao dịch phân tán. Chỉ nên tách khi ranh giới mới tạo ra quyền sở hữu hoặc khả năng mở rộng thực sự.
Vì sao cách làm đơn giản chưa đủ
Codebase lớn không tự động là lý do để tách microservices.
Modular monolith giữ cho các giao dịch nội bộ hoạt động nhanh và phản ánh thực tế đội ngũ có tôn trọng ranh giới module hay không.
Luồng xử lý
quyền sở hữu và quy tắc bảo toàn→Modular Monolith
thực thi ranh giới nghiêm ngặt→Điểm tách module
hợp đồng API và dữ liệu→Microservice
vận hành độc lập
Quyết định kiến trúc
Tách theo nghiệp vụ (Business Capability)
Góm gọn quy tắc nghiệp vụ và dữ liệu bảo vệ cùng một tính toàn vẹn vào một nơi.
Hạn chế gọi đồng bộ (Synchronous Call)
Chỉ sử dụng lời gọi đồng bộ khi bên gọi thực sự cần kết quả tức thì.
Quản lý toàn bộ vòng đời vận hành
Việc tách dịch vụ bao gồm cả trách nhiệm trực ban (on-call), bảng giám sát, triển khai, bảo mật và năng lực hạ tầng, không chỉ là chuyển mã nguồn sang repository khác.
Phân tích chuyên sâu
Quyền sở hữu dữ liệu là ranh giới khó nhất
Một dịch vụ có thể cung cấp mô hình đọc, sự kiện hoặc API. Tuy nhiên, dịch vụ khác không được phép ghi trực tiếp vào bảng của nó.
Chiến lược Strangler cần đối soát dữ liệu
Trong quá trình chuyển đổi, đối sánh kết quả giữa hệ thống cũ và mới, điều hướng một nhóm người dùng nhỏ và giữ đường quay lại nhanh chóng cho tới khi dữ liệu hoàn toàn khớp nhau.
Các bước triển khai
- Lập bản đồ nghiệp vụ, các quy tắc bảo toàn dữ liệu và quyền sở hữu thay đổi.
- Thực thi nghiêm ngặt API của module và các quy tắc phụ thuộc bên trong monolith.
- Tách một nghiệp vụ cụ thể kèm theo hợp đồng API, chiến lược di chuyển dữ liệu và đường quay lại (rollback).
Ví dụ kỹ thuật: Quy tắc phụ thuộc trước khi tách dịch vụ
module Orders exposes OrdersApi
module Billing exposes BillingApi
Orders may call BillingApi
Orders must not import BillingRepository
architectureTest.assertNoInternalDependencyLeaks()Các bài kiểm thử kiến trúc tự động biến sơ đồ module mong muốn thành ràng buộc ngay trong quá trình build.
Các tình huống lỗi cần tính trước
- Dùng chung cơ sở dữ liệu cho phép các dịch vụ đi vòng qua hợp đồng API.
- Ranh giới quá chi tiết gây ra bài toán phân tán N+1 lời gọi mạng.
- Hai đội ngũ cùng cho rằng mình sở hữu một quy tắc dữ liệu.
Tín hiệu cần giám sát
| Tín hiệu | Ý nghĩa |
|---|---|
| Tần suất thay đổi chéo module | Cho biết ranh giới vẫn yêu cầu các đợt phát hành phối hợp đồng thời. |
| Số lượng lời gọi từ xa mỗi luồng | Bộc lộ việc chia nhỏ hệ thống quá vụn khiến độ trễ tăng cao. |
| Độ độc lập khi triển khai và xử lý sự cố | Kiểm chứng việc tách dịch vụ có thực sự tạo ra sự tự chủ vận hành. |
Cách kiểm chứng thiết kế
- Chặn các truy vấn import nội bộ trái phép ngay trong quy trình CI.
- Thử nghiệm thêm độ trễ và lỗi giả lập vào ranh giới dự kiến trước khi chính thức tách.
- Chạy song song hai luồng xử lý với cùng dữ liệu đầu vào và đối soát kết quả.
Kế hoạch triển khai an toàn
Bắt đầu bằng bài kiểm thử kiến trúc và một module đã có chủ sở hữu rõ ràng. Tách luồng đọc, tiếp theo là luồng ghi, sau đó là dữ liệu.
Checklist trước khi đưa vào vận hành
- Mỗi dịch vụ toàn quyền sở hữu dữ liệu và các quyết định nghiệp vụ của riêng mình.
- Các lời gọi inter-dịch vụ có ngân sách độ trễ, thời gian chờ (timeout) và hạn mức lỗi rõ ràng.
- Nền tảng hạ tầng có thể triển khai, giám sát và bảo mật mọi dịch vụ một cách nhất quán.
Kết luận
Ranh giới dịch vụ tốt giúp giảm thiểu sự phối hợp. Ranh giới kém chỉ đơn thuần chuyển các lời gọi hàm thành gọi qua mạng.
