Microservices (kiến trúc vi dịch vụ) là cách xây dựng một ứng dụng thành nhiều dịch vụ nhỏ, mỗi dịch vụ phụ trách một năng lực nghiệp vụ, có dữ liệu riêng, giao tiếp qua mạng và được triển khai độc lập. Đối lập với nó là kiến trúc nguyên khối (monolith): toàn bộ ứng dụng là một khối mã, một cơ sở dữ liệu, triển khai cùng lúc.
Microservices không tốt hơn nguyên khối một cách tuyệt đối. Nó giải quyết vấn đề của tổ chức có nhiều đội phát triển cần thay đổi phần mềm độc lập và thường xuyên, đổi lại bằng độ phức tạp vận hành lớn hơn nhiều. Với phần lớn doanh nghiệp vừa và các sản phẩm ở giai đoạn đầu, một khối được chia mô-đun rõ ràng thường là lựa chọn hợp lý hơn. Hiểu điều này giúp nhà quản lý đánh giá đúng đề xuất kiến trúc của đội kỹ thuật hoặc nhà thầu.
Nguồn gốc và định nghĩa chuẩn
Thuật ngữ microservices được dùng trong giới kỹ thuật từ khoảng năm 2011–2012, và trở nên phổ biến sau bài viết tháng 3 năm 2014 của James Lewis và Martin Fowler. Bài viết không đưa ra định nghĩa chặt mà mô tả một nhóm đặc điểm thường gặp: tổ chức dịch vụ quanh năng lực nghiệp vụ thay vì quanh tầng kỹ thuật; mỗi dịch vụ tự quản lý dữ liệu; "đầu cuối thông minh, ống truyền đơn giản"; tự động hoá hạ tầng; thiết kế chấp nhận lỗi; và quản trị phân tán thay vì một chuẩn công nghệ áp xuống toàn tổ chức.
Về mặt ý tưởng, microservices kế thừa kiến trúc hướng dịch vụ (SOA) của thập niên 2000 nhưng phản ứng với cách SOA được triển khai — tập trung quanh một trục tích hợp nặng nề và các chuẩn giao thức phức tạp. Sam Newman, trong cuốn Building Microservices xuất bản năm 2015, mô tả vi dịch vụ là các dịch vụ nhỏ, tự chủ, cùng làm việc, với nhấn mạnh vào khả năng triển khai độc lập: thay đổi một dịch vụ và đưa vào vận hành mà không cần triển khai lại các dịch vụ khác.
Tiêu chí triển khai độc lập là phép thử hữu ích nhất. Nếu hệ thống được gọi là microservices nhưng mỗi lần phát hành phải đưa lên đồng thời mười dịch vụ theo đúng thứ tự, đó không phải microservices mà là một khối nguyên khối phân tán — mang toàn bộ chi phí của hệ phân tán mà không có lợi ích chính của nó.
Nguyên khối, nguyên khối mô-đun và vi dịch vụ
Giữa hai cực có một lựa chọn trung gian ngày càng được nhắc tới: nguyên khối mô-đun (modular monolith), một ứng dụng triển khai một lần nhưng bên trong chia mô-đun với ranh giới rõ, không cho mô-đun này truy cập thẳng dữ liệu của mô-đun khác.
| Tiêu chí | Nguyên khối truyền thống | Nguyên khối mô-đun | Vi dịch vụ |
|---|---|---|---|
| Đơn vị triển khai | Một | Một | Nhiều, độc lập |
| Cơ sở dữ liệu | Một, dùng chung tự do | Một, chia lược đồ theo mô-đun | Mỗi dịch vụ một kho riêng |
| Giao tiếp nội bộ | Gọi hàm trực tiếp | Gọi qua giao diện mô-đun | Gọi qua mạng, sự kiện |
| Độ phức tạp vận hành | Thấp | Thấp | Cao: giám sát phân tán, truy vết, quản lý cấu hình |
| Giao dịch dữ liệu | Giao dịch cơ sở dữ liệu đơn giản | Giao dịch đơn giản | Phải dùng nhất quán cuối cùng, bù trừ |
| Mở rộng tải | Nhân bản cả khối | Nhân bản cả khối | Mở rộng riêng từng dịch vụ |
| Phù hợp | Sản phẩm nhỏ, đội nhỏ | Phần lớn doanh nghiệp vừa, sản phẩm đang lớn | Tổ chức nhiều đội, tần suất phát hành cao |
Nguyên khối mô-đun giữ được phần lớn lợi ích về tổ chức mã nguồn, đồng thời để ngỏ khả năng tách một mô-đun thành dịch vụ riêng khi có lý do thật. Nhiều kiến trúc sư khuyên bắt đầu từ đây thay vì đi thẳng vào vi dịch vụ.
Định luật Conway: kiến trúc phản chiếu tổ chức
Năm 1968, Melvin Conway viết trong bài "How Do Committees Invent?" rằng tổ chức thiết kế hệ thống sẽ tạo ra những thiết kế sao chép cấu trúc giao tiếp của chính tổ chức đó. Nhận định này về sau được gọi là định luật Conway và trở thành lập luận trung tâm cho vi dịch vụ: nếu muốn nhiều đội làm việc độc lập, phần mềm phải được chia sao cho mỗi đội sở hữu trọn một phần, từ mã nguồn tới dữ liệu tới vận hành.
Lập luận này cũng có chiều ngược lại mà ít được nhắc tới. Nếu doanh nghiệp chỉ có một đội phát triển năm sáu người, chia phần mềm thành hai mươi dịch vụ không tạo ra sự độc lập nào — cùng năm người đó phải vận hành hai mươi thứ thay vì một. Kiến trúc vi dịch vụ là lời giải cho bài toán phối hợp giữa nhiều đội; không có bài toán đó, nó chỉ còn lại chi phí.
Câu hỏi thực dụng khi xem một đề xuất kiến trúc vì thế không phải "có hiện đại không" mà là: bao nhiêu đội sẽ làm việc trên hệ thống này trong ba năm tới, và họ có cần phát hành độc lập không? Một doanh nghiệp thuê ngoài phát triển phần mềm quản lý nội bộ với một nhà thầu duy nhất hiếm khi có lý do để chọn vi dịch vụ.
Các tổ chức thường được nhắc tới như hình mẫu vi dịch vụ đều có chung một đặc điểm: hàng trăm, hàng nghìn kỹ sư chia thành nhiều đội nhỏ, mỗi đội chịu trách nhiệm trọn vẹn một dịch vụ kể cả trực sự cố ban đêm. Nguyên tắc "đội hai chiếc bánh pizza" gắn với Amazon — đội đủ nhỏ để hai chiếc bánh pizza là đủ ăn — phản ánh đúng logic đó: kích thước dịch vụ đi theo kích thước đội, không đi theo sở thích kỹ thuật. Sao chép kiến trúc của họ mà không có cấu trúc tổ chức tương ứng là sao chép phần chi phí mà bỏ lại phần lợi ích.
Chi phí ẩn của vi dịch vụ
Gọi qua mạng thay vì gọi hàm. Mỗi lời gọi giữa hai dịch vụ có thể chậm, lỗi hoặc treo. Mã nguồn phải xử lý hết chờ, thử lại, ngắt mạch — những thứ không tồn tại trong một khối.
Giao dịch trải trên nhiều dịch vụ. Đặt hàng, trừ tồn kho, trừ điểm thưởng nằm ở ba dịch vụ thì không còn một giao dịch cơ sở dữ liệu bao trọn. Phải thiết kế chuỗi bước có hành động bù trừ khi một bước thất bại, phức tạp hơn nhiều.
Giám sát và truy vết. Một yêu cầu của người dùng đi qua sáu dịch vụ; khi chậm, phải có hệ thống truy vết phân tán để biết chậm ở đâu. Không có công cụ này, gỡ lỗi là đoán mò.
Báo cáo tổng hợp. Dữ liệu nằm rải ở nhiều kho riêng; muốn báo cáo doanh thu theo khách hàng phải gom về kho dữ liệu, thêm một tầng tích hợp nữa.
Hạ tầng và kỹ năng. Cần đóng gói, điều phối container, quản lý cấu hình và bí mật cho từng dịch vụ, cùng đội ngũ có kỹ năng vận hành tương ứng. Chi phí đám mây cũng tăng vì mỗi dịch vụ có tài nguyên tối thiểu riêng.
Khi lùi về nguyên khối là quyết định đúng
Trào lưu vi dịch vụ mạnh nhất trong khoảng 2015–2020, và sau đó xuất hiện nhiều phản tư từ chính các nhóm đã áp dụng. Một trường hợp được thảo luận rộng rãi là bài viết năm 2023 của một nhóm kỹ thuật tại Amazon Prime Video, mô tả việc chuyển một dịch vụ giám sát chất lượng video từ kiến trúc phân tán gồm nhiều thành phần serverless về một ứng dụng chạy liền khối, qua đó giảm mạnh chi phí hạ tầng. Nhóm này nhấn mạnh quyết định là cho một bài toán cụ thể, không phải phủ nhận vi dịch vụ nói chung.
Điều đáng rút ra là chiều hướng tranh luận đã chuyển từ "nguyên khối hay vi dịch vụ" sang "ranh giới đặt ở đâu và với chi phí nào". Một số tổ chức gộp các dịch vụ chia quá nhỏ — mỗi dịch vụ chỉ vài trăm dòng mã nhưng phải gọi nhau liên tục — thành các dịch vụ lớn hơn theo miền nghiệp vụ. Một số khác giữ lõi nguyên khối và chỉ tách những phần có nhu cầu mở rộng hoặc tốc độ thay đổi rất khác, như tìm kiếm, xử lý ảnh, gửi thông báo.
Với bên đặt hàng phần mềm, bài học là không nên coi "dùng vi dịch vụ" là tiêu chí chấm điểm trong hồ sơ mời thầu. Hãy yêu cầu nhà thầu giải thích ranh giới mô-đun, chiến lược dữ liệu, cách giám sát và chi phí vận hành dự kiến; kiến trúc phù hợp sẽ lộ ra từ những câu trả lời đó.
Tiêu chí quyết định tách một phần thành dịch vụ riêng
Nếu đã có một hệ thống và đang cân nhắc tách dần, có thể dùng chuỗi câu hỏi dưới đây cho từng ứng viên. Chỉ nên tách khi trả lời "có" cho ít nhất một trong ba câu đầu và chuẩn bị được câu thứ tư.
- Tốc độ thay đổi khác biệt: phần này cần phát hành nhiều lần mỗi tuần trong khi phần còn lại ổn định hằng tháng?
- Nhu cầu tải khác biệt: phần này cần mở rộng gấp nhiều lần vào giờ cao điểm trong khi phần còn lại thì không?
- Đội sở hữu riêng: có một đội cụ thể sẽ chịu trách nhiệm trọn vẹn phần này, từ phát triển đến trực sự cố?
- Ranh giới dữ liệu rõ: phần này có thể sở hữu dữ liệu riêng mà phần còn lại chỉ truy cập qua giao diện, không cần truy vấn chéo?
- Năng lực vận hành sẵn sàng: đã có giám sát, truy vết, quy trình triển khai tự động cho ít nhất một dịch vụ độc lập?
Ranh giới dịch vụ nên đi theo ranh giới nghiệp vụ — khái niệm "ngữ cảnh giới hạn" (bounded context) trong phương pháp thiết kế hướng miền nghiệp vụ của Eric Evans, công bố năm 2003 — chứ không theo tầng kỹ thuật như giao diện, xử lý, cơ sở dữ liệu. Tách theo tầng gần như chắc chắn tạo ra những dịch vụ phải gọi nhau ở mọi thao tác.
Câu hỏi thường gặp
Microservices và API khác nhau thế nào?
API là giao diện để phần mềm giao tiếp; microservices là cách chia một ứng dụng. Các vi dịch vụ dùng API hoặc sự kiện để nói chuyện với nhau, nhưng một ứng dụng nguyên khối cũng có thể cung cấp API ra bên ngoài. Có API không có nghĩa là đang dùng vi dịch vụ.
Phần mềm ERP thương mại có phải vi dịch vụ không?
Phần lớn ERP lâu đời có lõi nguyên khối với các phân hệ chia trong cùng hệ thống. Một số sản phẩm thế hệ mới tách một phần thành dịch vụ riêng trên đám mây. Với người dùng doanh nghiệp, điều quan trọng hơn kiến trúc bên trong là API, khả năng mở rộng và chính sách nâng cấp.
Serverless có phải microservices không?
Serverless là mô hình vận hành, trong đó nhà cung cấp đám mây tự cấp phát tài nguyên khi có yêu cầu và tính phí theo lượt chạy. Có thể dùng serverless để xây vi dịch vụ, nhưng cũng có thể chạy một ứng dụng nguyên khối trên nền tảng serverless. Hai khái niệm độc lập với nhau.
Chuyển từ nguyên khối sang vi dịch vụ nên bắt đầu từ đâu?
Thường bắt đầu bằng việc chia mô-đun rõ ràng ngay trong khối hiện tại, tách dữ liệu theo mô-đun, rồi chọn một phần có ranh giới rõ và lợi ích cụ thể để tách ra trước. Viết lại toàn bộ cùng lúc là cách có rủi ro cao nhất.
Vi dịch vụ có bảo mật hơn nguyên khối không?
Không tự động. Vi dịch vụ cho phép cô lập tốt hơn, một dịch vụ bị xâm nhập không nhất thiết lộ toàn bộ dữ liệu. Nhưng bề mặt tấn công rộng hơn vì có nhiều điểm giao tiếp qua mạng, nhiều bí mật cần quản lý và nhiều thành phần cần vá lỗi.
Doanh nghiệp vừa có nên yêu cầu nhà thầu xây phần mềm theo vi dịch vụ không?
Chỉ khi có lý do cụ thể như nhiều đội phát triển song song hoặc một phần có tải đột biến. Nếu không, một ứng dụng nguyên khối chia mô-đun tốt, có API rõ ràng và tài liệu đầy đủ thường rẻ hơn, nhanh hơn và dễ chuyển giao cho nhà thầu khác.