Báo cáo dễ hiểu về kiến trúc dịch vụ nhỏ
Ngày viết lại: 30/05/2026 Nguồn gốc: viết lại từ bản báo cáo domain-book/bao-cao-kien-truc-va-bai-toan-kho-microservices.md theo phong cách dễ hiểu hơn. Mục tiêu: giải thích kiến trúc dịch vụ nhỏ bằng tiếng Việt dễ hiểu, hạn chế tiếng Anh và từ học thuật khó. Ghi chú: trong bản này, “dịch vụ nhỏ” được dùng thay cho thuật ngữ gốc. Một vài tên chuẩn/tài liệu vẫn được giữ ở phần nguồn.
1. Nói ngắn gọn trước
Dịch vụ nhỏ không khó vì có nhiều dịch vụ.
Nó khó vì:
tách sai ranh giới thì mọi thứ đắt hơn;
dữ liệu thuộc về sai nơi thì đồng bộ rất đau;
hợp đồng gọi nhau không rõ thì các đội phụ thuộc nhau;
sự kiện viết sai thì mỗi nơi hiểu một kiểu;
không lần theo được lỗi thì sửa lỗi rất mù;
đưa bản mới lên riêng nhưng bật tính năng vẫn phải họp cả công ty;
và nếu đội chưa đủ trưởng thành,
hệ chỉ biến một khối rối thành nhiều khối nhỏ rối hơn.
Một hệ một khối rối có thể khó sửa.
Một hệ nhiều dịch vụ nhỏ rối có thể:
- khó sửa hơn vì một yêu cầu đi qua nhiều chỗ,
- khó đưa bản mới hơn vì nhiều phụ thuộc,
- khó kiểm thử hơn vì môi trường phức tạp,
- khó tìm lỗi hơn vì lỗi chạy qua nhiều dịch vụ,
- khó bảo mật hơn vì có nhiều đường gọi nội bộ,
- khó giữ dữ liệu đúng hơn vì nhiều cơ sở dữ liệu,
- khó hiểu hơn vì luật nghiệp vụ bị xé nhỏ,
- khó vận hành hơn vì nhiều cảnh báo, nhiều bảng theo dõi, nhiều người trực,
- khó thay đổi hơn vì giao tiếp giữa các đội không rõ.
Điểm cần nhớ:
Dịch vụ nhỏ không phải mục tiêu.
Nó là cái giá ta trả để đổi lấy:
- đội tự đưa thay đổi nhanh hơn,
- ranh giới trách nhiệm rõ hơn,
- lỗi của một phần ít kéo sập phần khác hơn,
- và từng năng lực có thể lớn lên theo cách riêng.
Nếu không lấy được các lợi ích đó,
ta chỉ đang mua thêm độ phức tạp.
Một hệ dịch vụ nhỏ chạy thật phải trả lời được:
- Dịch vụ được tách theo năng lực nghiệp vụ hay theo bảng dữ liệu?
- Mỗi dịch vụ có thật sự đưa bản mới lên riêng được không?
- Mỗi dịch vụ sở hữu dữ liệu nào?
- Dịch vụ khác có đọc thẳng cơ sở dữ liệu của nó không?
- Hợp đồng gọi nhau có phiên bản không?
- Đổi cấu trúc dữ liệu có làm hỏng bên đang dùng không?
- Luồng đi qua nhiều dịch vụ xử lý thế nào khi một bước lỗi?
- Khi vừa đổi dữ liệu vừa gửi sự kiện, có bị lệch không?
- Một yêu cầu có lần theo được qua nhiều dịch vụ không?
- Nếu dịch vụ A chờ quá lâu, dịch vụ B thử lại thế nào?
- Cổng vào hoặc lớp điều phối giao tiếp đang giải quyết vấn đề thật nào?
- Kiểm thử gồm những lớp nào?
- Ai trực và chịu trách nhiệm cho dịch vụ?
- Đội có quyền đưa bản mới lên và quay lại bản cũ không?
- Nếu từ 10 dịch vụ lên 200 dịch vụ, nền vận hành có chịu nổi không?
- Khi nào nên gộp hai dịch vụ lại?
Điểm quan trọng:
Dịch vụ nhỏ thành công khi:
ranh giới đúng,
dữ liệu đúng chủ,
đưa bản mới độc lập,
và đội sở hữu rõ.
Chỉ tách mã nguồn mà không tách trách nhiệm và dữ liệu
thì chưa phải kiến trúc trưởng thành.
2. Dịch vụ nhỏ là gì?
Một cách hiểu thực dụng:
Dịch vụ nhỏ là một phần hệ thống có thể đưa bản mới lên riêng,
được thiết kế quanh một năng lực nghiệp vụ rõ,
sở hữu dữ liệu và vòng đời của mình,
và giao tiếp với phần khác bằng hợp đồng rõ ràng.
Không nên gọi là dịch vụ nhỏ nếu nó chỉ là:
- một bộ điều khiển nhỏ,
- một kho mã riêng,
- một tiến trình riêng,
- một dịch vụ chỉ bọc quanh một bảng dữ liệu,
- đưa bản mới lên riêng nhưng dùng chung cơ sở dữ liệu lộn xộn,
- đưa bản mới lên riêng nhưng lúc bật tính năng vẫn cần nhiều đội cùng đổi,
- không có đội sở hữu,
- không có người trực,
- luật nghiệp vụ chính nằm ở một lớp điều phối trung tâm khổng lồ.
Ba điều quan trọng nhất:
đưa bản mới độc lập
+ ranh giới nghiệp vụ rõ
+ dữ liệu thuộc đúng chủ.
Thiếu một trong ba điều này, hệ rất dễ thành:
một khối phân tán.
Tức là nhìn bên ngoài có nhiều dịch vụ, nhưng thực chất vẫn phụ thuộc chặt như một khối lớn.
3. Dịch vụ nhỏ giải quyết bài toán gì?
Dịch vụ nhỏ có ích khi:
- nhiều đội cần làm và đưa thay đổi độc lập,
- nghiệp vụ có nhiều vùng rõ ràng,
- mỗi vùng thay đổi với tốc độ khác nhau,
- mỗi phần cần chịu tải khác nhau,
- cần giới hạn vùng ảnh hưởng khi lỗi,
- có ranh giới bảo mật hoặc tuân thủ khác nhau,
- có lý do thật để dùng cách chạy khác nhau,
- doanh nghiệp cần đưa một phần ra mà không phải đưa toàn hệ ra cùng lúc.
Ví dụ:
- tìm kiếm cần chỉ mục và xếp hạng riêng,
- thanh toán cần sổ tiền và đối chiếu riêng,
- xử lý video cần luồng mã hóa riêng,
- quản lý danh tính cần luật quyền và dấu vết riêng,
- thông báo cần gửi nhiều, thử lại nhiều, không làm chậm đơn hàng.
Dịch vụ nhỏ không giải quyết tốt nếu vấn đề thật là:
- chưa hiểu nghiệp vụ,
- đội chưa biết tự vận hành,
- kiểm thử yếu,
- đưa bản mới còn thủ công,
- dữ liệu đang rối,
- không rõ ai sở hữu phần nào,
- không có cách nhìn lỗi,
- hệ một khối còn chưa có ranh giới mô-đun.
Trong những trường hợp đó, tách nhỏ chỉ làm vấn đề to hơn.
4. Khi nào chưa nên dùng dịch vụ nhỏ?
Chưa nên dùng sớm khi:
- đội còn nhỏ,
- chưa có nhiều đội làm song song,
- nghiệp vụ còn đổi liên tục,
- chưa biết ranh giới nên tách ở đâu,
- hệ một khối chưa gây đau thật về tốc độ giao hàng,
- chưa có kiểm thử và đưa bản mới tự động đáng tin,
- chưa có cách lần theo lỗi,
- dữ liệu vẫn là một nguồn chung ai cũng đọc,
- doanh nghiệp chưa cần đưa từng phần lên riêng,
- điểm nghẽn hiệu năng chưa được đo,
- lý do chính chỉ là “cho giống công ty lớn”.
Nói thẳng:
Nếu chưa vận hành nổi một hệ một khối tử tế,
khả năng cao chưa vận hành nổi 30 dịch vụ nhỏ.
Lựa chọn thực dụng hơn khi bắt đầu:
hệ một khối nhưng chia mô-đun rõ,
ranh giới nội bộ rõ,
sự kiện nội bộ có kỷ luật,
dữ liệu không bị đọc lộn xộn,
và chỉ tách thành dịch vụ riêng khi ranh giới đã chứng minh là đúng.
5. Hệ một khối có mô-đun là bước đệm tốt
Hệ một khối có mô-đun không phải thất bại.
Nó là cách:
- giữ việc đưa bản mới đơn giản,
- giữ giao dịch trong một cơ sở dữ liệu dễ hơn,
- tìm lỗi dễ hơn,
- nhưng vẫn ép ranh giới nghiệp vụ rõ.
Một hệ một khối có mô-đun tốt có:
- các mô-đun theo vùng nghiệp vụ,
- giao diện công khai rõ giữa mô-đun,
- không đọc dữ liệu của mô-đun khác lung tung,
- sự kiện nội bộ,
- luật phụ thuộc rõ,
- kiểm thử theo mô-đun,
- người hoặc đội sở hữu từng mô-đun,
- có đường tách ra thành dịch vụ riêng sau này.
Bẫy:
- gọi là mô-đun nhưng mô-đun nào cũng gọi mô-đun nào,
- dùng chung quá nhiều kiểu dữ liệu,
- cấu trúc bảng lẫn lộn,
- luật nghiệp vụ nằm trong thư viện chung,
- không có kiểm tra phụ thuộc,
- không có kỷ luật khi phát sự kiện.
Cách đi tốt:
Chia mô-đun tốt trước.
Tách dịch vụ sau khi ranh giới nghiệp vụ và ranh giới đội đã ổn định.
6. Ranh giới dịch vụ: tách theo bảng là bẫy lớn
Ranh giới tốt thường dựa trên:
- năng lực nghiệp vụ,
- vùng nghiệp vụ có ngôn ngữ riêng,
- luật bất biến riêng,
- dữ liệu thuộc riêng,
- đội sở hữu riêng,
- tốc độ thay đổi riêng,
- rủi ro hoặc tuân thủ riêng,
- nhu cầu chịu tải riêng.
Ranh giới xấu thường dựa trên:
- bảng dữ liệu,
- danh từ trong hệ,
- lớp kỹ thuật,
- màn hình giao diện,
- suy nghĩ “dịch vụ phải nhỏ”,
- cơ cấu đội tạm thời,
- thư viện hoặc khung lập trình.
Ví dụ tên dịch vụ chưa chắc sai, nhưng dễ bị hiểu sai:
UserService
OrderService
PaymentService
CourseService
StatusService
ReportService
Nếu chỉ đặt theo danh từ hoặc bảng, ta vẫn chưa hiểu ranh giới nghiệp vụ.
Câu hỏi tốt hơn:
- Vùng này có ngôn ngữ riêng không?
- Nó có luật riêng không?
- Nó có dữ liệu riêng không?
- Nó có tốc độ thay đổi riêng không?
- Đội nào chịu trách nhiệm từ đầu tới cuối?
- Tách ra có giảm phối hợp hay làm tăng phối hợp?
Dịch vụ nhỏ nên là phần triển khai của một ranh giới đã hiểu.
Không nên dùng sản xuất thật để “tìm ranh giới bằng đau khổ”.
7. Sở hữu của đội: dịch vụ nhỏ cũng là thiết kế tổ chức
Dịch vụ nhỏ không chỉ là thiết kế kỹ thuật.
Nó còn là cách chia trách nhiệm giữa các đội.
Một dịch vụ nên có:
- đội sở hữu,
- mục tiêu rõ,
- thông tin trong danh mục dịch vụ,
- hợp đồng gọi vào/gửi sự kiện,
- người trực,
- bảng theo dõi,
- mục tiêu mức phục vụ,
- hướng dẫn xử lý sự cố,
- người chịu chi phí,
- người chịu bảo mật,
- kế hoạch phát triển.
Bẫy:
- một đội sở hữu 40 dịch vụ,
- dịch vụ không có chủ,
- đội nền tảng phải duyệt mọi lần đưa bản mới,
- các đội phụ thuộc nhau qua tin nhắn riêng,
- đổi một hợp đồng gọi nhau cần họp nhiều đội,
- người trực nhận cảnh báo nhưng không có quyền sửa.
Nguyên tắc:
Nếu một dịch vụ không có đội sở hữu đủ quyền vận hành,
đó không phải ranh giới tốt.
8. Đưa bản mới độc lập là phép thử thật
Một dịch vụ thật sự độc lập khi:
- tự xây,
- tự kiểm thử,
- tự đưa bản mới lên,
- không cần đưa dịch vụ khác lên cùng lúc,
- hợp đồng cũ vẫn tương thích,
- đổi dữ liệu không làm hỏng bên đang dùng,
- có cấu hình riêng,
- có cờ bật/tắt tính năng riêng,
- quay lại bản cũ được,
- có đủ theo dõi để biết bản mới ổn không,
- đội có quyền đưa ra.
Không độc lập nếu:
- phải đưa giao diện, cổng vào, dịch vụ và cơ sở dữ liệu lên cùng lúc,
- đổi thư viện chung làm 20 dịch vụ phải đưa lên lại,
- sự kiện đổi kiểu làm bên nhận hỏng,
- kiểm thử toàn hệ là cửa duy nhất,
- cơ sở dữ liệu dùng chung làm đổi bảng chặn nhiều đội,
- toàn công ty vẫn phải theo một lịch đưa bản mới.
Phép thử:
Dịch vụ A có thể đưa bản mới lúc 14:00
mà dịch vụ B và C không cần biết, không cần đổi, không cần đưa lên lại không?
Nếu không, hãy gọi nó là một mô-đun phân tán trước khi gọi là dịch vụ nhỏ.
9. Mỗi dịch vụ nên sở hữu dữ liệu riêng
Một ý chính của dịch vụ nhỏ:
Dịch vụ nào sở hữu dữ liệu thì dịch vụ đó giữ quyền thay đổi dữ liệu.
Dịch vụ khác không đọc thẳng cơ sở dữ liệu của nó.
Lý do:
- bảo vệ quyền sở hữu dữ liệu,
- giảm phụ thuộc vào cấu trúc bảng,
- cho phép đổi cấu trúc dữ liệu độc lập,
- giữ luật nghiệp vụ trong đúng dịch vụ.
Nhược điểm:
- truy vấn xuyên nhiều dịch vụ khó hơn,
- giao dịch nhiều dịch vụ khó hơn,
- báo cáo cần mô hình đọc riêng,
- có thể phải sao chép dữ liệu,
- dữ liệu sẽ có lúc chưa đồng bộ ngay,
- chuyển dữ liệu khi tách ra phức tạp.
Bẫy lớn:
Dịch vụ A có cơ sở dữ liệu riêng,
nhưng dịch vụ B đọc thẳng bảng của A để làm báo cáo.
Đó là ranh giới giả.
Cách làm tốt:
Cơ sở dữ liệu riêng.
Đường gọi hoặc sự kiện công khai.
Mô hình đọc riêng cho báo cáo/tìm kiếm.
Hợp đồng dữ liệu cho bên dùng sau.
Đối chiếu cho luồng quan trọng.
10. Giao dịch qua nhiều dịch vụ: tránh trước, rồi mới xử lý
Khi mỗi dịch vụ giữ dữ liệu riêng, một thao tác dài không còn dễ gói trong một giao dịch duy nhất.
Ví dụ thanh toán đơn:
Đơn hàng
-> Thanh toán
-> Tồn kho
-> Giao hàng
-> Thông báo
Không nên mặc định dùng một cơ chế khóa tất cả để cùng thành công hoặc cùng thất bại.
Lý do:
- phụ thuộc khi chạy rất chặt,
- hệ dễ kém sẵn sàng,
- khóa tài nguyên khó,
- không hợp khi có hệ bên ngoài,
- nhiều tác động ngoài cơ sở dữ liệu không thể đảo lại sạch.
Cách thường dùng là chia luồng thành nhiều bước nhỏ.
Mỗi bước tự ghi dữ liệu của mình.
Nếu bước sau lỗi, hệ có bước bù trừ hoặc xử lý ngoại lệ.
Ví dụ:
Giữ tiền trước.
Giữ hàng trước.
Nếu hết hàng thì thả tiền.
Nếu thanh toán lỗi thì thả hàng.
Nếu đã gửi hàng thì không thể "hoàn tác" như xóa bản ghi.
Có hai cách điều phối:
- các dịch vụ tự phản ứng khi thấy sự kiện,
- có một bộ điều phối luồng nhiều bước.
Bẫy:
- bước bù trừ không thật sự đảo được tác động,
- sự kiện bị mất hoặc bị gửi trùng,
- luồng nhiều bước không có trạng thái rõ,
- các dịch vụ tự phản ứng làm luồng khó hiểu,
- bộ điều phối thành một dịch vụ khổng lồ biết mọi thứ,
- người dùng thấy trạng thái trung gian nhưng sản phẩm không thiết kế thông báo rõ.
Nguyên tắc:
Nếu một luật bắt buộc phải đúng tức thì, hãy xem lại ranh giới.
Nếu luồng dài, hãy mô hình hóa luồng đó rõ ràng.
Nếu tác động không đảo được, hãy dùng giữ trước, xác nhận sau.
11. Bảng chờ gửi sự kiện gần như bắt buộc
Bài toán rất hay gặp:
Đổi dữ liệu thành công.
Nhưng gửi sự kiện thất bại.
Hoặc:
Gửi sự kiện thành công.
Nhưng đổi dữ liệu bị hủy.
Khi đó hệ khác có thể nhận sự kiện không khớp với dữ liệu thật.
Cách làm tốt:
Trong cùng một giao dịch:
đổi dữ liệu nghiệp vụ
ghi thêm một bản ghi sự kiện chờ gửi
Sau khi giao dịch xong:
một tiến trình khác đọc bảng chờ
gửi sự kiện ra ngoài
bên nhận xử lý sao cho nhận trùng cũng không hỏng
Ví dụ:
BẮT ĐẦU
cập nhật trạng thái đơn
thêm sự kiện "Đơn đã đặt"
KẾT THÚC
bộ gửi đọc sự kiện chờ -> gửi cho bên khác
Bẫy:
- bên nhận không chịu được sự kiện trùng,
- sự kiện thiếu mã đối tượng hoặc phiên bản,
- thứ tự gửi không rõ,
- bảng chờ không được dọn,
- gửi lại làm tạo tác động phụ,
- kiểu sự kiện không có phiên bản,
- dùng bảng chờ để ghi mọi thứ linh tinh.
Cách này không làm hệ “chính xác đúng một lần”.
Nó giúp việc đổi dữ liệu và gửi sự kiện đáng tin hơn.
12. Sự kiện là hợp đồng, không phải dòng nhật ký
Sự kiện tốt là chuyện nghiệp vụ đã xảy ra.
Ví dụ:
ĐơnĐãĐặt
ThanhToánĐãĐượcGiữ
QuyềnHọcĐãĐượcCấp
KhóaHọcĐãHoànThành
HoànTiềnĐãTạo
Sự kiện kém:
ĐơnĐãCậpNhật
TrạngTháiĐổi
DữLiệuĐổi
CóChuyệnXảyRa
Một sự kiện công khai cần có:
- tên,
- đội tạo ra,
- cấu trúc dữ liệu,
- ý nghĩa rõ,
- phiên bản,
- mã đối tượng,
- thời điểm xảy ra,
- mã liên kết với yêu cầu gốc,
- khóa chống xử lý trùng,
- mức nhạy cảm dữ liệu,
- thời hạn lưu,
- ai đang dùng.
Bẫy:
- sự kiện chỉ là bản sao của một dòng trong bảng,
- tên sự kiện mơ hồ,
- không biết hệ nào là nơi đúng cuối cùng,
- đổi cấu trúc làm bên nhận hỏng,
- mỗi bên nhận hiểu một kiểu,
- không có chủ sở hữu,
- chủ đề sự kiện thành bãi rác tích hợp.
Nguyên tắc:
Sự kiện công khai cũng là hợp đồng.
Đối xử với nó nghiêm túc như đường gọi dịch vụ.
13. Hợp đồng gọi nhau cần phiên bản
Dịch vụ sống bằng hợp đồng.
Hợp đồng gồm:
- đường gọi,
- dữ liệu gửi/nhận,
- ý nghĩa,
- quyền gọi,
- giới hạn tốc độ,
- lỗi trả về,
- cách chống gửi trùng,
- phân trang,
- tương thích ngược,
- thời gian bỏ phiên bản cũ.
Bẫy:
- chỉ đổi đường dẫn nhưng ý nghĩa lại đổi ngầm,
- xóa trường mà bên đang dùng vẫn cần,
- mã lỗi mỗi nơi một kiểu,
- hành vi khi quá thời gian chờ không rõ,
- chống gửi trùng không được ghi rõ,
- cổng vào che mất đội sở hữu thật,
- nghĩ “nội bộ thôi” nên không cần hợp đồng.
Cách làm tốt:
- ưu tiên thay đổi theo kiểu thêm, không phá cái cũ,
- bên nhận phải chịu được trường chưa biết,
- có thời gian báo trước khi bỏ cái cũ,
- kiểm tra tương thích tự động nếu cần,
- hợp đồng và chủ sở hữu nằm trong danh mục dịch vụ.
Đường gọi nội bộ vẫn là đường gọi sản xuất.
14. Gọi trực tiếp qua mạng không phải gọi hàm trong mã
Khi tách dịch vụ, gọi nhau qua mạng trở thành một phần của nghiệp vụ.
Gọi qua mạng có:
- độ trễ,
- hết thời gian chờ,
- lỗi một phần,
- gửi lại,
- giới hạn tốc độ,
- xác thực,
- chuyển đổi dữ liệu,
- phiên bản,
- theo dõi.
Bẫy:
- chuỗi gọi quá dài,
- thời gian chờ mặc định quá lâu,
- gửi lại không có độ ngẫu nhiên nên gây dồn tải,
- gửi lại một lệnh không an toàn làm tạo tác động phụ,
- một yêu cầu gọi chéo rất nhiều lần,
- độ sẵn sàng của A bị nhân với B, C, D,
- cổng gom dữ liệu thành điểm nghẽn,
- thư viện dùng chung làm các dịch vụ phụ thuộc chặt.
Cách làm tốt:
- đặt ngân sách thời gian rõ,
- có cách ngắt khi bên kia lỗi nặng,
- tách vùng lỗi,
- có cách giảm tải,
- lệnh quan trọng phải chịu được gửi lại,
- tránh chuỗi gọi trực tiếp cho luồng dài,
- dùng mô hình đọc hoặc sự kiện cho trường hợp cần phát cho nhiều bên.
Nói ngắn:
Gọi dịch vụ khác là gọi qua mạng.
Qua mạng thì luôn có trễ, lỗi, gửi trùng và mất kết nối.
15. Cổng vào và cổng riêng cho từng giao diện
Cổng vào là nơi gom lối vào cho ứng dụng hoặc khách hàng bên ngoài.
Nó thường xử lý:
- chuyển yêu cầu tới đúng dịch vụ,
- kiểm tra đăng nhập ở biên,
- giới hạn tốc độ,
- đổi dạng yêu cầu,
- ghi theo dõi,
- chặn tấn công phổ biến,
- gom nhẹ dữ liệu trả về.
Cổng riêng cho từng giao diện nghĩa là mỗi loại giao diện có một lớp phục vụ riêng.
Ví dụ:
- ứng dụng điện thoại cần dữ liệu khác trang quản trị,
- màn hình khách cần dữ liệu khác màn hình nhân viên,
- trang web cần cách gom khác ứng dụng nhỏ.
Bẫy:
- cổng vào chứa luật nghiệp vụ,
- cổng vào gọi quá nhiều dịch vụ cùng lúc,
- đội cổng vào thành điểm nghẽn,
- cổng riêng lặp lại luật nghiệp vụ,
- chỉ kiểm quyền ở cổng vào rồi các dịch vụ bên trong tin nhau mù quáng,
- cổng vào che lỗi phía sau quá kém.
Nguyên tắc:
Cổng vào tối ưu cho ranh giới với người dùng hoặc ứng dụng.
Quyền quyết định nghiệp vụ vẫn thuộc dịch vụ nghiệp vụ.
16. Tìm dịch vụ, chia tải và phụ thuộc khi chạy
Trong hệ nhiều dịch vụ, các bản chạy có thể thay đổi liên tục.
Cần:
- nơi biết dịch vụ nào đang sống,
- chia tải,
- kiểm tra sức khỏe,
- phân biệt sống và sẵn sàng nhận việc,
- gửi lại hợp lý,
- hết thời gian chờ,
- ngắt khi lỗi kéo dài,
- chuyển tải dần,
- hiểu vùng/khu vực.
Bẫy:
- kiểm tra sức khỏe chỉ trả “200” nhưng phụ thuộc chính đã chết,
- không tách “còn sống” với “sẵn sàng nhận việc”,
- bộ chia tải gửi việc tới bản đang khởi động,
- chuyển vùng làm vùng còn lại quá tải,
- thư viện phía gọi mỗi nơi một phiên bản,
- bộ nhớ đệm thông tin dịch vụ bị cũ.
Cách làm tốt:
- định nghĩa rõ thế nào là khỏe,
- tách sống, sẵn sàng, đang khởi động,
- đặt chính sách chuyển tải có chủ ý,
- theo dõi cả phía gọi và phía được gọi.
17. Lớp điều phối giao tiếp giữa dịch vụ
Khi số dịch vụ nhiều, có thể cần một lớp hạ tầng chuyên xử lý giao tiếp giữa dịch vụ.
Lớp này có thể giúp:
- mã hóa và xác thực giữa dịch vụ,
- định danh dịch vụ,
- định tuyến lưu lượng,
- gửi lại và hết thời gian chờ,
- ngắt khi lỗi,
- thu số liệu,
- áp chính sách,
- đưa bản mới dần dần,
- tiến gần hơn tới kiểu “không tin mặc định”.
Nhưng nó cũng thêm:
- độ phức tạp vận hành,
- độ trễ,
- cấu hình khó,
- lớp lỗi mới,
- gánh nặng cho đội nền tảng,
- khó tìm lỗi hơn nếu chưa quen.
Bẫy:
- dùng lớp này để che ứng dụng không có thời gian chờ và chống gửi trùng,
- cấu hình phức tạp hơn chính ứng dụng,
- mã hóa bật nhưng phân quyền yếu,
- chính sách gửi lại tạo bão yêu cầu,
- có số liệu hạ tầng rồi tưởng đã đủ nhìn vào hệ.
Nguyên tắc:
Chỉ dùng khi số lượng dịch vụ, rủi ro và năng lực vận hành đủ lớn.
Không dùng nó để thay kỷ luật thiết kế.
18. Không nhìn được thì vận hành như mù
Dịch vụ nhỏ cần khả năng nhìn vào hệ thống ngay từ đầu.
Cần:
- lần theo một yêu cầu qua nhiều dịch vụ,
- nhật ký có cấu trúc,
- chỉ số,
- mã liên kết yêu cầu,
- mã nguyên nhân,
- bản đồ phụ thuộc,
- chủ sở hữu dịch vụ,
- mục tiêu mức phục vụ,
- dấu mốc đưa bản mới,
- sự kiện nghiệp vụ quan trọng.
Bẫy:
- mỗi dịch vụ có nhật ký riêng nhưng không nối được,
- yêu cầu qua hàng đợi thì mất dấu,
- luồng bất đồng bộ không biết nguyên nhân gốc,
- chỉ số thiếu vùng, khách thuê, dịch vụ,
- quá nhiều nhãn làm chi phí tăng mạnh,
- bảng theo dõi theo dịch vụ nhưng sự cố theo hành trình người dùng,
- cảnh báo không gửi đúng đội.
Cách làm tốt:
Lần theo theo hành trình người dùng, không chỉ theo từng dịch vụ.
Nối được gọi trực tiếp và gọi qua hàng đợi.
Ghi sự kiện nghiệp vụ cho trạng thái quan trọng.
Mỗi dịch vụ có chủ, bảng theo dõi, mục tiêu mức phục vụ và hướng dẫn xử lý.
19. Kiểm thử: đừng bắt kiểm thử toàn hệ gánh hết
Dịch vụ nhỏ làm kiểm thử phức tạp hơn.
Các lớp kiểm thử nên có:
- kiểm thử hàm nhỏ,
- kiểm thử luật nghiệp vụ,
- kiểm thử một dịch vụ độc lập,
- kiểm thử hợp đồng giữa bên gọi và bên cung cấp,
- kiểm thử tích hợp với hệ thật cần thiết,
- kiểm thử vài hành trình trọng yếu từ đầu tới cuối,
- kiểm thử trên môi trường thật bằng yêu cầu giả an toàn,
- kiểm thử chịu lỗi.
Bẫy:
- kiểm thử toàn hệ là cửa chính cho mọi thay đổi,
- môi trường kiểm thử không giống thật,
- hợp đồng không được kiểm thử,
- bản giả không khớp bên cung cấp thật,
- kiểm thử chập chờn quá nhiều,
- mỗi thay đổi phải dựng cả hệ,
- chuyển dữ liệu chỉ kiểm thử đường vui.
Cách làm tốt:
Kiểm thử một dịch vụ và hợp đồng phải mạnh.
Kiểm thử toàn hệ chỉ giữ vài hành trình quan trọng.
Yêu cầu giả trên hệ thật giúp kiểm tra phụ thuộc thật.
Nếu đưa bản mới của một dịch vụ mà phải kiểm thử toàn công ty, tính độc lập chưa đạt.
20. Đưa bản mới lên khác với bật tính năng cho người dùng
Một dịch vụ có thể đưa bản mới lên riêng, nhưng bật tính năng vẫn phụ thuộc:
- hợp đồng gọi nhau,
- đổi dữ liệu,
- giao diện,
- cờ bật/tắt,
- bên dùng đã sẵn sàng chưa,
- bên nhận sự kiện,
- thay đổi chính sách,
- tài liệu và hỗ trợ.
Cách làm tốt:
- đổi dữ liệu theo kiểu mở rộng trước, thu hẹp sau,
- thay đổi hợp đồng tương thích ngược,
- có cờ bật/tắt tính năng,
- chạy ngầm trước khi bật,
- mở cho nhóm nhỏ,
- mở dần,
- có cách quay lại,
- có dấu mốc đưa bản mới trong bảng theo dõi.
Bẫy:
- đưa thay đổi phá hợp đồng,
- quay lại mã nhưng cấu trúc dữ liệu không quay lại được,
- cờ tính năng để mãi không dọn,
- mở thử chỉ nhìn CPU mà không nhìn lỗi nghiệp vụ,
- lịch đưa bản mới chung toàn hệ vì hợp đồng yếu,
- thứ tự đưa bản mới nằm trong trang tài liệu thủ công.
Nguyên tắc:
Đưa bản mới lên là thao tác kỹ thuật.
Bật tính năng là quyết định sản phẩm.
Hai việc này nên tách nhau.
21. Bảo mật: bên trong cũng không được tin mặc định
Dịch vụ nhỏ làm bề mặt tấn công tăng:
- nhiều đường gọi,
- nhiều danh tính dịch vụ,
- nhiều bí mật,
- nhiều đường mạng,
- nhiều thư viện phụ thuộc,
- nhiều ảnh chạy,
- nhiều luồng đưa bản mới.
Cần:
- định danh dịch vụ,
- mã hóa giữa dịch vụ khi phù hợp,
- cấp quyền tối thiểu,
- kiểm quyền ở ranh giới dịch vụ,
- xoay bí mật,
- danh tính cho tác vụ chạy,
- phạm vi quyền cho từng đường gọi,
- dấu vết kiểm tra,
- kiểm soát nguồn gốc bản dựng,
- quét lỗ hổng.
Bẫy:
- tin toàn bộ mạng nội bộ,
- chỉ kiểm quyền ở cổng vào,
- dùng chung một mã truy cập cho nhiều dịch vụ,
- bí mật nằm trong biến môi trường rồi lọt ra nhật ký,
- tài khoản dịch vụ quá nhiều quyền,
- bản dựng chưa ký vẫn được đưa lên,
- lỗ hổng thư viện không biết thuộc dịch vụ nào.
Cách làm tốt:
Mỗi lần gọi biết ai gọi.
Mỗi hành động nhạy cảm kiểm quyền lại.
Mỗi bản dựng biết nguồn gốc.
Mỗi dịch vụ chạy với quyền tối thiểu.
22. Báo cáo và truy vấn dữ liệu
Dịch vụ nhỏ làm dữ liệu vận hành bị phân tán.
Nhưng doanh nghiệp vẫn cần:
- bảng theo dõi,
- tìm kiếm,
- báo cáo,
- màn hỗ trợ khách hàng,
- kiểm toán,
- phân tích,
- huấn luyện mô hình.
Bẫy:
- công cụ báo cáo đọc thẳng cơ sở dữ liệu của dịch vụ,
- màn hỗ trợ nối nhiều cơ sở dữ liệu thật,
- chỉ mục tìm kiếm không kiểm quyền,
- mô hình đọc không có chủ sở hữu,
- kho dữ liệu không biết nguồn gốc,
- báo cáo dùng dữ liệu chậm nhưng không nói rõ độ mới.
Cách làm tốt:
- tạo mô hình đọc từ sự kiện,
- dùng kho dữ liệu cho báo cáo,
- có hợp đồng dữ liệu,
- dùng bảng chờ gửi sự kiện hoặc bắt thay đổi có kiểm soát,
- mô hình đọc có kiểm quyền,
- có mục tiêu độ mới dữ liệu,
- có đối chiếu.
Dịch vụ nhỏ thường cần nền dữ liệu mạnh hơn hệ một khối.
Vì dữ liệu không còn nằm trong một cơ sở dữ liệu dễ nối.
23. Phiên bản và tương thích
Trong hệ nhiều dịch vụ, thay đổi phá vỡ không chỉ là thay đổi mã.
Nó là một sự kiện phối hợp giữa đội.
Cần quản lý phiên bản cho:
- đường gọi,
- sự kiện,
- cấu trúc dữ liệu,
- cấu hình,
- cờ tính năng,
- thư viện cho bên dùng,
- luật chính sách,
- luồng nhiều bước.
Bẫy:
- ghi v2 nhưng ý nghĩa vẫn đổi ngầm,
- khách dùng cũ không có ngày dừng,
- bên nhận sự kiện không biết bên gửi đã đổi,
- thư viện chung lệch phiên bản,
- dùng lại mã trường cũ cho nghĩa mới,
- thêm giá trị mới làm bên nhận hỏng,
- bỏ phiên bản cũ nhưng không có chủ.
Cách làm tốt:
- ưu tiên thêm, không phá,
- bên nhận chịu được dữ liệu lạ,
- kiểm tra tương thích cấu trúc,
- có thời gian báo trước,
- theo dõi ai còn dùng trước khi bỏ,
- có chính sách phiên bản trong danh mục dịch vụ.
24. Dịch vụ quá nhỏ cũng là lỗi
Dịch vụ quá lớn có vấn đề:
- nhiều chủ sở hữu,
- nhiều lý do thay đổi,
- đưa bản mới chậm,
- lỗi ảnh hưởng rộng.
Dịch vụ quá nhỏ cũng có vấn đề:
- phụ thuộc khi chạy quá nhiều,
- gọi qua lại quá nhiều,
- luật nghiệp vụ bị xé vụn,
- chi phí vận hành cao,
- kiểm thử phức tạp,
- đội không đủ sức sở hữu.
Không có kích thước chuẩn.
Nên hỏi:
- Dịch vụ có năng lực nghiệp vụ rõ không?
- Dịch vụ có luật riêng không?
- Dịch vụ có đưa bản mới độc lập không?
- Dịch vụ có dữ liệu riêng không?
- Đội có vận hành nổi không?
- Sơ đồ gọi nhau có hợp lý không?
Nguyên tắc:
Dịch vụ đủ nhỏ để một đội hiểu và đưa bản mới độc lập,
nhưng đủ lớn để giữ một trách nhiệm nghiệp vụ có ý nghĩa.
25. Dịch vụ nhỏ cần nền vận hành mạnh
Hệ nhiều dịch vụ cần nền vận hành:
- danh mục dịch vụ,
- tự động xây và đưa bản mới,
- nơi lưu bản dựng,
- quản lý môi trường,
- quản lý bí mật,
- quản lý cấu hình,
- tìm dịch vụ,
- theo dõi,
- chính sách,
- xử lý sự cố,
- chia chi phí,
- quét bảo mật,
- khuôn mẫu tạo dịch vụ mới.
Bẫy:
- mỗi đội tự chọn cách ghi nhật ký, theo dõi, đưa bản mới,
- không có danh mục dịch vụ,
- không biết dịch vụ nào gọi dịch vụ nào,
- kịch bản đưa bản mới sao chép thủ công,
- bí mật và cấu hình trôi dạt,
- người trực không khớp dịch vụ,
- không chia được chi phí.
Cách làm tốt:
Đường chuẩn để tạo dịch vụ mới.
Theo dõi mặc định.
Bảo mật mặc định.
Đưa bản mới mặc định.
Cho phép thoát đường chuẩn khi có lý do và có người chịu trách nhiệm.
Dịch vụ nhỏ làm nhu cầu về nền vận hành xuất hiện sớm hơn và mạnh hơn.
26. Dịch vụ nhỏ có chi phí thật
Dịch vụ nhỏ làm tăng chi phí:
- máy chạy nhiều hơn,
- gọi qua mạng nhiều hơn,
- dữ liệu theo dõi nhiều hơn,
- thời gian kiểm thử và đưa bản mới nhiều hơn,
- môi trường kiểm thử phức tạp hơn,
- cần đội nền tảng,
- lớp điều phối giao tiếp có chi phí,
- công vận hành tăng,
- phối hợp khi sự cố khó hơn,
- thư viện và môi trường chạy bị lặp.
Nhưng có thể giảm chi phí nếu:
- từng năng lực có thể lớn lên riêng,
- tách phần ồn ào khỏi phần còn lại,
- tối ưu dịch vụ nóng,
- giảm phối hợp khi đưa bản mới,
- giảm vùng ảnh hưởng khi lỗi.
Bẫy:
- tách dịch vụ nhưng tải của chúng luôn tăng giảm cùng nhau,
- mỗi dịch vụ có cơ sở dữ liệu riêng quá đắt mà không có lợi ích tương xứng,
- nhật ký và theo dõi tăng theo số dịch vụ,
- chi phí nền tảng vượt giá trị kinh doanh,
- không đo chi phí theo dịch vụ hoặc năng lực.
Nguyên tắc:
Mỗi dịch vụ phải trả được tiền thuê nhà của nó:
độc lập giao hàng,
độc lập chịu tải,
cô lập rủi ro,
hoặc làm rõ nghiệp vụ.
27. Chuyển từ hệ một khối: làm dần, không đập đi xây lại
Chuyển tốt thường theo:
hiểu hệ thống
-> chia mô-đun
-> chọn điểm tách
-> chuyển một phần lưu lượng
-> đồng bộ dữ liệu
-> chạy song song
-> chuyển hẳn
-> gỡ đường cũ.
Bẫy:
- viết lại toàn bộ,
- tách dịch vụ nhưng cơ sở dữ liệu vẫn dùng chung không có kế hoạch,
- ghi hai nơi nhưng không đối chiếu,
- dịch vụ mới vẫn gọi ngầm vào hệ cũ,
- hệ cũ vẫn là nguồn đúng nhưng dịch vụ mới tưởng mình sở hữu dữ liệu,
- không có đường quay lại,
- không có lát cắt nghiệp vụ nhỏ để chứng minh.
Cách làm tốt:
- bắt đầu từ điểm tách rủi ro thấp nhưng có giá trị,
- tạo lớp chống lẫn mô hình cũ và mới,
- tạm dùng lớp bọc dữ liệu nếu cần,
- dùng bảng chờ gửi sự kiện hoặc bắt thay đổi có kiểm soát,
- đọc bóng để so kết quả,
- chuyển từng bước,
- đo bằng số liệu,
- có kế hoạch gỡ phần cũ.
Chuyển sang dịch vụ nhỏ là thay đổi kinh doanh và vận hành, không chỉ là sửa mã.
28. Một khối phân tán: dấu hiệu nhận biết
Dấu hiệu:
- dịch vụ nào đổi cũng phải đưa dịch vụ khác lên,
- cơ sở dữ liệu dùng chung,
- chuỗi gọi trực tiếp dài,
- một giao dịch nghiệp vụ cần nhiều dịch vụ cùng đổi,
- kiểm thử toàn hệ là cửa chính,
- dịch vụ không có chủ,
- một bộ điều phối trung tâm chứa luật của mọi vùng,
- thay đổi phá hợp đồng xảy ra thường xuyên,
- đưa bản mới phải theo thứ tự trong tài liệu,
- không lần theo lỗi được,
- một dịch vụ lỗi kéo sập toàn hệ.
Nguyên nhân:
- ranh giới sai,
- dữ liệu sai chủ,
- đội sở hữu sai,
- nền vận hành yếu,
- chuyển đổi nửa vời,
- dùng dịch vụ nhỏ để né kỷ luật mô-đun.
Cách sửa:
- xem lại vùng nghiệp vụ,
- tách quyền sở hữu dữ liệu,
- quản lý phiên bản hợp đồng,
- giảm gọi trực tiếp,
- dùng sự kiện và mô hình đọc,
- rõ chủ sở hữu,
- có đường chuẩn từ nền vận hành,
- có thể gộp dịch vụ lại.
Gộp dịch vụ lại không phải thất bại.
Đôi khi đó là dấu hiệu kiến trúc trưởng thành hơn.
29. Dịch vụ nhỏ ở quy mô nhiều vùng
Khi hệ chạy nhiều vùng hoặc nhiều quốc gia, dịch vụ nhỏ có thêm vấn đề:
- chọn vùng xử lý,
- dữ liệu được phép ở đâu,
- đưa bản mới theo từng vùng,
- sự kiện đi qua vùng,
- đổi cấu trúc dữ liệu theo từng đợt,
- tìm dịch vụ toàn cầu,
- chuyển vùng khi sự cố,
- độ trễ giữa vùng,
- nguy cơ hai vùng cùng nghĩ mình là chính,
- đặt khách thuê vào vùng nào,
- theo dõi theo vùng.
Bẫy:
- dịch vụ A chạy nhiều vùng, dịch vụ B chỉ chạy một vùng,
- gọi trực tiếp xuyên vùng,
- sao chép sự kiện không chịu được trùng,
- dữ liệu bị lộ qua nhật ký/sự kiện sang vùng không được phép,
- chuyển vùng không biết dịch vụ phía sau ở đâu,
- đưa bản mới toàn cầu cùng lúc làm vùng ảnh hưởng quá lớn.
Cách làm tốt:
Đường quan trọng nên tự chủ trong từng vùng nếu có thể.
Hạn chế gọi trực tiếp xuyên vùng.
Chia khách hoặc ô hệ thống để giới hạn vùng lỗi.
Đưa thay đổi theo vùng hoặc theo nhóm nhỏ.
Dịch vụ nhỏ không tự làm hệ toàn cầu dễ hơn.
Nó chỉ cho nhiều nút điều khiển hơn. Nhiều nút hơn cũng dễ vặn sai hơn.
30. Bảng kiểm khi xem kiến trúc dịch vụ nhỏ
30.1. Lý do
- Vì sao cần dịch vụ nhỏ?
- Vấn đề là tốc độ giao hàng, chịu tải, sở hữu, tuân thủ hay đội?
- Hệ một khối có mô-đun đã đủ chưa?
- Lợi ích có lớn hơn độ phức tạp không?
30.2. Ranh giới
- Dịch vụ theo năng lực nghiệp vụ chưa?
- Có dữ liệu riêng không?
- Có luật riêng không?
- Ranh giới có khớp đội sở hữu không?
- Có dịch vụ quá nhỏ hoặc quá kỹ thuật không?
30.3. Dữ liệu
- Dịch vụ khác có đọc thẳng cơ sở dữ liệu không?
- Giao dịch qua nhiều dịch vụ xử lý thế nào?
- Có bảng chờ gửi sự kiện và chống xử lý trùng không?
- Báo cáo, tìm kiếm, màn hỗ trợ lấy dữ liệu thế nào?
- Luồng quan trọng có đối chiếu không?
30.4. Hợp đồng
- Đường gọi và sự kiện có chủ và phiên bản không?
- Chính sách tương thích ngược là gì?
- Có cần kiểm thử hợp đồng theo bên dùng không?
- Có thời gian báo trước khi bỏ phiên bản cũ không?
- Có theo dõi ai còn dùng không?
30.5. Khi chạy
- Thời gian chờ và gửi lại đã chuẩn chưa?
- Sơ đồ gọi nhau có quá sâu không?
- Tìm dịch vụ, chia tải, kiểm tra sức khỏe có rõ không?
- Vùng lỗi và vùng ảnh hưởng là gì?
30.6. Nhìn vào hệ
- Lần theo được yêu cầu qua gọi trực tiếp và hàng đợi không?
- Nhật ký có cấu trúc và mã liên kết không?
- Có mục tiêu mức phục vụ theo dịch vụ và theo hành trình người dùng không?
- Cảnh báo có gửi đúng đội không?
30.7. Đưa bản mới
- Dịch vụ có đưa bản mới độc lập thật không?
- Xây một lần rồi đẩy qua các môi trường được không?
- Quay lại và đổi dữ liệu có an toàn không?
- Bật tính năng có cờ và mở dần không?
30.8. Bảo mật
- Dịch vụ có danh tính không?
- Gọi nội bộ có xác thực và phân quyền không?
- Bí mật có xoay được không?
- Bản dựng có nguồn gốc và chữ ký không?
- Quyền khi chạy có tối thiểu không?
30.9. Tổ chức
- Đội nào sở hữu dịch vụ?
- Đội có đủ sức trực không?
- Nền vận hành hỗ trợ đủ không?
- Danh mục dịch vụ có đúng không?
- Tải nhận thức của đội có quá nặng không?
31. Mô hình tư duy dễ nhớ: DỊCH VỤ
Có thể nhớ bằng sáu câu hỏi.
31.1. D - Dịch vụ này đại diện cho ranh giới nào?
- Nó là năng lực nghiệp vụ nào?
- Nó thuộc vùng nghiệp vụ nào?
- Nó có luật riêng không?
- Dữ liệu nào thuộc nó?
- Tách ra làm giảm hay tăng phối hợp?
31.2. I - Ít phụ thuộc khi chạy không?
- Sơ đồ gọi nhau có sâu không?
- Gọi trực tiếp hay qua sự kiện?
- Có thời gian chờ, gửi lại và giảm tải không?
- Dịch vụ nào là phụ thuộc sống còn?
- Lỗi lan như thế nào?
31.3. C - Có hợp đồng rõ không?
- Đường gọi có phiên bản không?
- Sự kiện có phiên bản không?
- Có kiểm tra tương thích không?
- Có theo dõi bên dùng không?
- Thay đổi phá vỡ xử lý thế nào?
31.4. H - Hàng rào dữ liệu có rõ không?
- Cơ sở dữ liệu có riêng không?
- Ranh giới giao dịch là gì?
- Luồng nhiều bước có trạng thái không?
- Có bảng chờ gửi sự kiện không?
- Báo cáo và tìm kiếm lấy dữ liệu thế nào?
31.5. V - Vận hành nhìn thấy được không?
- Có lần theo yêu cầu không?
- Sự kiện có mã liên kết không?
- Dịch vụ có bảng theo dõi không?
- Có mục tiêu mức phục vụ không?
- Có hướng dẫn xử lý sự cố không?
31.6. U - Ủy quyền sở hữu và bảo mật rõ không?
- Ai là chủ?
- Ai trực?
- Đội có quyền đưa bản mới và quay lại không?
- Danh tính dịch vụ và phân quyền ra sao?
- Nền vận hành có đủ không?
Tóm tắt:
Niềm tin vào dịch vụ nhỏ =
ranh giới đúng
+ dữ liệu đúng chủ
+ hợp đồng rõ
+ đưa bản mới độc lập
+ nhìn được khi lỗi
+ đội sở hữu và bảo mật rõ.
32. Lộ trình áp dụng thực dụng
32.1. Giai đoạn 1: hệ một khối có kỷ luật
Mục tiêu:
- từ vựng nghiệp vụ rõ,
- ranh giới mô-đun rõ,
- luật phụ thuộc rõ,
- sự kiện nội bộ,
- bảng chờ sự kiện cho việc quan trọng,
- tự động kiểm thử và đưa bản mới tốt,
- theo dõi cơ bản.
Không nên làm:
- tách ồ ạt,
- dùng hàng đợi, lớp điều phối giao tiếp hoặc nền chạy phức tạp nếu chưa cần,
- giả vờ dữ liệu riêng nhưng vẫn đọc chung.
32.2. Giai đoạn 2: tìm điểm tách có giá trị
Mục tiêu:
- tìm vùng nghiệp vụ rõ,
- tìm điểm đau: tốc độ giao hàng, chịu tải, rủi ro,
- chọn ứng viên dịch vụ,
- lập kế hoạch dữ liệu thuộc về ai,
- lập kế hoạch hợp đồng,
- lập kế hoạch chuyển đổi.
Tiêu chí chọn dịch vụ đầu tiên:
- ranh giới nghiệp vụ rõ,
- ít giao dịch chặt với phần còn lại,
- giá trị kinh doanh rõ,
- có đội sở hữu,
- có đường quay lại hoặc chuyển đổi kiểm soát được.
32.3. Giai đoạn 3: tách dịch vụ đầu tiên cho đúng
Mục tiêu:
- hợp đồng gọi hoặc sự kiện rõ,
- dữ liệu riêng,
- bảng chờ gửi sự kiện,
- mô hình đọc nếu cần,
- lần theo lỗi được,
- có người trực và hướng dẫn xử lý,
- đưa bản mới riêng,
- có đường xử lý sự cố.
32.4. Giai đoạn 4: xây nền vận hành
Mục tiêu:
- danh mục dịch vụ,
- đường chuẩn tạo dịch vụ,
- theo dõi mặc định,
- ghi nhật ký và chỉ số mặc định,
- định danh dịch vụ,
- khuôn đưa bản mới,
- kiểm thử hợp đồng,
- quét bảo mật,
- nhìn được chi phí.
32.5. Giai đoạn 5: mở rộng hệ sinh thái dịch vụ
Mục tiêu:
- quản trị đường gọi và sự kiện,
- vòng đời dịch vụ,
- điểm số sở hữu,
- lớp điều phối giao tiếp nếu thật sự cần,
- chiến lược theo vùng hoặc nhóm khách,
- kỷ luật bỏ phiên bản cũ hoặc gộp dịch vụ lại,
- kiểm tra kiến trúc tự động nếu có thể.
33. Thực hành tốt hiện nay
33.1. Ranh giới và nghiệp vụ
Cách làm tốt:
- tìm vùng nghiệp vụ rõ,
- lập bản đồ năng lực kinh doanh,
- bắt đầu bằng hệ một khối có mô-đun,
- tách theo điểm nghiệp vụ có giá trị,
- khớp ranh giới với đội.
Đánh đổi:
- hiểu nghiệp vụ tốn thời gian,
- ranh giới có thể đổi,
- cơ cấu đội cũng phải đổi.
33.2. Dữ liệu và nhất quán
Cách làm tốt:
- mỗi dịch vụ giữ dữ liệu riêng,
- bảng chờ gửi sự kiện,
- luồng nhiều bước có trạng thái,
- bên nhận chịu được sự kiện trùng,
- mô hình đọc,
- hợp đồng dữ liệu,
- đối chiếu.
Đánh đổi:
- dữ liệu có thể chậm khớp,
- báo cáo phức tạp hơn,
- vận hành nặng hơn,
- tìm lỗi khó hơn.
33.3. Đường gọi và sự kiện
Cách làm tốt:
- hợp đồng rõ trước khi dùng rộng,
- kiểm tra tương thích cấu trúc,
- kiểm thử hợp đồng,
- cổng vào hoặc cổng riêng giao diện khi thật sự hữu ích.
Đánh đổi:
- thêm công quản trị,
- nhiều phiên bản hơn,
- phải bảo trì kiểm thử hợp đồng.
33.4. Khi chạy và nền vận hành
Cách làm tốt:
- tìm dịch vụ và chia tải rõ,
- đưa bản mới dần dần,
- danh mục dịch vụ,
- lần theo yêu cầu,
- chỉ dùng lớp điều phối giao tiếp khi xứng đáng.
Đánh đổi:
- nền vận hành phức tạp,
- chi phí cao hơn,
- cần kỹ năng vận hành tốt.
33.5. Bảo mật
Cách làm tốt:
- danh tính cho tác vụ chạy,
- tiến tới không tin mặc định,
- quyền tối thiểu,
- ký và biết nguồn gốc bản dựng,
- danh sách thành phần phần mềm,
- chính sách trước khi đưa lên,
- bảo mật khi chạy.
Đánh đổi:
- người làm có thể thấy nhiều rào chắn hơn,
- chính sách có thể gây nhiễu,
- chuyển đổi hệ cũ phức tạp.
34. Những bẫy cần tránh
34.1. Bẫy “dùng dịch vụ nhỏ để mở rộng”
Mở rộng cái gì?
- lưu lượng,
- đội,
- dữ liệu,
- rủi ro,
- vùng địa lý?
Nếu không rõ, dịch vụ nhỏ là thuốc quá mạnh.
34.2. Bẫy tách theo bảng dữ liệu
Bảng không phải vùng nghiệp vụ.
Tách theo bảng thường tạo nhiều dịch vụ rỗng nghĩa và gọi nhau liên tục.
34.3. Bẫy dùng chung cơ sở dữ liệu
Dùng chung cơ sở dữ liệu biến ranh giới dịch vụ thành lời nói.
34.4. Bẫy coi sự kiện như nhật ký đổ ra ngoài
Sự kiện là hợp đồng nghiệp vụ, không phải “dòng dữ liệu đã đổi”.
34.5. Bẫy luồng nhiều bước không có trạng thái
Luồng phân tán mà không biết đang ở bước nào sẽ rất khó sửa và khôi phục.
34.6. Bẫy kiểm thử toàn hệ gánh mọi thứ
Kiểm thử toàn hệ làm đưa bản mới chậm và dễ chập chờn.
34.7. Bẫy dùng lớp điều phối giao tiếp quá sớm
Lớp đó giải quyết vấn đề giao tiếp ở nền vận hành.
Nó không giải quyết ranh giới nghiệp vụ sai.
34.8. Bẫy để việc nhìn lỗi sau cùng
Dịch vụ nhỏ mà không lần theo được yêu cầu thì gần như không vận hành được.
34.9. Bẫy độc lập giả
Đưa bản mới riêng nhưng bật tính năng vẫn phải cùng nhau thì vẫn là phụ thuộc.
34.10. Bẫy không có nền vận hành
Mỗi đội tự chế kiểm thử, đưa bản mới, ghi nhật ký và bảo mật thì độ hỗn loạn tăng rất nhanh.
34.11. Bẫy không gộp lại khi cần
Nếu hai dịch vụ luôn đổi cùng nhau, đưa lên cùng nhau và lỗi cùng nhau, có thể ranh giới sai.
34.12. Bẫy quên con người
Dịch vụ nhỏ là kiến trúc cho tổ chức.
Đội không chịu nổi thì kiến trúc không bền.
35. Kết luận
Dịch vụ nhỏ là công cụ mạnh, nhưng không phải cấp tiến hóa bắt buộc của mọi hệ thống.
Người mới thường thấy:
nhiều dịch vụ,
kho mã riêng,
vùng chạy riêng,
công cụ triển khai hiện đại.
Người làm chắc phải thấy:
ranh giới nghiệp vụ,
quyền sở hữu dữ liệu,
hợp đồng giao tiếp,
đưa bản mới độc lập,
khả năng nhìn lỗi,
đội sở hữu,
bảo mật,
và chi phí vận hành.
Một kiến trúc dịch vụ nhỏ tốt không bắt đầu bằng “tách nhỏ”.
Nó bắt đầu bằng:
- ranh giới nghiệp vụ rõ,
- đội sở hữu rõ,
- dữ liệu thuộc đúng chủ,
- hợp đồng rõ,
- nền vận hành đủ,
- khả năng nhìn lỗi đủ,
- đưa bản mới đủ trưởng thành,
- và lý do kinh doanh/kỹ thuật đủ mạnh để trả chi phí phân tán.
Câu chốt:
Dịch vụ nhỏ tốt là khi mỗi dịch vụ là một ranh giới sống:
có nghiệp vụ rõ,
dữ liệu rõ,
hợp đồng rõ,
chủ sở hữu rõ,
đưa bản mới độc lập,
nhìn được khi lỗi,
khôi phục được,
và làm giảm phối hợp hơn so với trước.
36. Nguồn nền đã dùng trong bản gốc
Các nguồn dưới đây là tên tác giả, tiêu chuẩn, tài liệu hoặc công cụ nên vẫn giữ nguyên:
- Martin Fowler và James Lewis: microservices, distributed objects, prerequisites, bounded context và strangler pattern.
- CNCF Cloud Native Glossary: microservices architecture và service mesh.
- Microservices.io: database per service, saga, transactional outbox, API gateway, service discovery.
- AWS và Thoughtworks: hướng dẫn triển khai, event-driven, evolutionary architecture.
- Sam Newman: Backends for Frontends và tư duy dịch vụ theo nghiệp vụ.
- OpenAPI và AsyncAPI: mô tả hợp đồng của đường gọi và sự kiện.
- OpenTelemetry: lần theo yêu cầu, chỉ số và nhật ký.
- Istio, SPIFFE: service mesh và danh tính dịch vụ.
- DORA, Google SRE, Team Topologies, SLSA và NIST SSDF: hiệu quả đưa bản mới, độ tin cậy, đội sở hữu và bảo mật chuỗi phần mềm.
Liên kết tham khảo:
- Martin Fowler, Microservices: https://martinfowler.com/articles/microservices.html
- Microservices and the First Law of Distributed Objects: https://www.martinfowler.com/articles/distributed-objects-microservices.html
- Microservice Prerequisites: https://martinfowler.com/bliki/MicroservicePrerequisites.html
- Bounded Context: https://martinfowler.com/bliki/BoundedContext.html
- Strangler Fig Application: https://martinfowler.com/bliki/StranglerFigApplication.html
- CNCF Microservices Architecture: https://glossary.cncf.io/microservices-architecture/
- CNCF Service Mesh: https://glossary.cncf.io/service-mesh/
- Microservices.io patterns: https://microservices.io/patterns/index.html
- Database per Service: https://microservices.io/patterns/data/database-per-service.html
- Saga: https://microservices.io/patterns/data/saga.html
- Transactional Outbox: https://microservices.io/patterns/data/transactional-outbox.html
- API Gateway: https://microservices.io/patterns/apigateway.html
- Independently deployable: https://microservices.io/post/architecture/2022/05/04/microservice-architecture-essentials-deployability.html
- Debezium Outbox Event Router: https://debezium.io/documentation/reference/stable/transformations/outbox-event-router.html
- AWS Implementing Microservices: https://docs.aws.amazon.com/whitepapers/latest/microservices-on-aws/microservices-on-aws.html
- Thoughtworks Microservices: https://www.thoughtworks.com/en-us/insights/decoder/m/microservices
- Microservices as an Evolutionary Architecture: https://www.thoughtworks.com/insights/blog/microservices-evolutionary-architecture
- Sam Newman, Backends For Frontends: https://samnewman.io/patterns/architectural/bff/
- Sam Newman: https://samnewman.io/
- OpenAPI Specification: https://learn.openapis.org/specification/
- AsyncAPI Specification: https://www.asyncapi.com/docs/reference/specification/v3.0.0
- OpenTelemetry Documentation: https://opentelemetry.io/docs/
- Istio Documentation: https://istio.io/latest/docs/
- SPIFFE: https://spiffe.io/
- DORA State of DevOps Report 2024: https://dora.dev/report/2024/
- Google SRE Book, Service Level Objectives: https://sre.google/sre-book/service-level-objectives/
- Team Topologies: https://teamtopologies.com/
- SLSA: https://slsa.dev/
- NIST Secure Software Development Framework: https://csrc.nist.gov/pubs/sp/800/218/final