Máy pha cà phê trong văn phòng đôi khi là nơi yên tĩnh nhất để nghe một quyết định kiến trúc bắt đầu thành hình. Ai đó đứng đó với ly giấy, đợi dòng cà phê chảy chậm, rồi nói một câu rất quen: team checkout lại bị kẹt vì team catalog chưa sẵn sàng release. Một tuần sau, chữ microservices xuất hiện trong tài liệu planning, chưa hẳn như một lựa chọn công nghệ, mà như một hy vọng rằng các team cuối cùng có thể bớt phải chờ nhau.
Hy vọng đó rất dễ hiểu. Một monolith đang lớn có thể trở nên nặng. Một deployment pipeline phục vụ mọi team. Một database chung trở thành nơi mọi shortcut và hotfix khẩn cấp lắng xuống. Một thay đổi nhỏ ở billing cũng có thể làm onboarding thấy lo. Cuộc họp bớt nói về sản phẩm, và nói nhiều hơn về việc tìm một cửa sổ release không làm ai đau quá. Khi người ta nói muốn microservices, nhiều khi họ đang xin một điều rất con người: có thêm không gian để di chuyển.
Phần khó là microservices hiếm khi làm độ phức tạp biến mất. Nó chuyển độ phức tạp sang chỗ khác. Code bên trong từng service có thể nhỏ hơn, nhưng hệ thống giữa các service lại lớn hơn. Một lời gọi hàm trở thành lời gọi mạng. Một transaction trở thành saga hoặc workflow eventually consistent. Một stack trace cục bộ trở thành một trace đi qua năm service, ba queue và hai dashboard. Chi phí không tự biến mất chỉ vì repository nhìn gọn hơn.
Chi phí đầu tiên là vận hành. Mỗi service cần build, deployment, configuration, secret management, health check, alert, log stream, dashboard, rollback path và owner. Một service thì dễ chăm. Mười service cần kỷ luật. Năm mươi service cần một platform. Nếu chưa có platform đó, engineer sẽ dành ít thời gian hơn cho sản phẩm và nhiều thời gian hơn để nhớ service nào đang sở hữu failure mode nào. Microservices đòi tổ chức phải giỏi vận hành trước khi kiến trúc bắt đầu nhẹ đi.
Chi phí thứ hai là dữ liệu. Trong monolith, consistency thường nhàm chán theo nghĩa tốt nhất. Một database transaction có thể cập nhật các record liên quan cùng lúc, và code dựa được vào ranh giới đó. Trong microservices, mỗi service nên sở hữu data riêng, nghĩa là hệ thống phải chấp nhận độ trễ, dữ liệu lặp, và cơ chế sửa sai. Một order có thể được tạo trước khi invoice hiện ra. Một payment có thể thành công trong khi notification service đang down. Đây là các vấn đề giải được, nhưng không miễn phí. Chúng cần event design cẩn thận, idempotency, reconciliation và cả ngôn ngữ sản phẩm giúp người dùng hiểu những trạng thái tạm thời.
Chi phí thứ ba là observability. Khi user nói trang bị chậm, câu trả lời không còn nằm trong một log file. Nó có thể nằm trong một chuỗi: API gateway, auth service, order service, inventory service, payment service, message broker, email worker. Để debug mà không đoán mò, team cần trace ID, structured logs, metrics, SLO và đủ thực hành chung để đọc chúng khi có áp lực. Observability không phải đồ trang trí cho microservices. Nó là một phần của kiến trúc.
Chi phí thứ tư là testing. Unit test vẫn hữu ích, nhưng nhiều lỗi quan trọng sống ở ranh giới giữa các service: một API contract đổi, một field trong event bị thiếu, một timeout chỉ xuất hiện khi dependency phía dưới chậm. Team cần contract test, integration environment, fixture gần thực tế, và thói quen nghĩ về failure path. Nếu không, hệ thống chỉ chạy tốt khi mọi service cư xử lịch sự, mà đó không phải giả định đáng tin.
Chi phí thứ năm là phối hợp, dù phối hợp chính là thứ microservices hứa sẽ giảm. Team chỉ deploy độc lập được khi contract ổn định, ownership rõ ràng, và dependency được nhìn thẳng. Nếu mỗi service vẫn cần ba team khác release trước, tổ chức chưa thoát khỏi monolith. Nó chỉ xây một phòng chờ phiên bản distributed của cùng một vấn đề.
Vì vậy câu hỏi hữu ích nhất không phải là, có nên dùng microservices không? Câu hỏi tốt hơn là, hiện tại mình đang trả chi phí nào, và mình đã sẵn sàng trả chi phí tiếp theo chưa? Modular monolith có thể là câu trả lời khôn ngoan trong một thời gian dài. Nó cho team boundary rõ hơn, ownership tốt hơn, code sạch hơn, mà không bắt mọi thay đổi sản phẩm đi qua network. Đôi khi kiến trúc tử tế nhất là kiến trúc giữ những thứ đơn giản tiếp tục đơn giản, đồng thời chuẩn bị các seam sạch cho ngày thật sự cần tách.
Microservices bắt đầu hợp lý hơn khi nỗi đau không còn là lý thuyết. Team bị chặn bởi shared deployment mỗi tuần. Một vài domain cần scale độc lập. Failure cần được cô lập vì một phần sản phẩm rủi ro hơn hoặc bận hơn phần còn lại. Tổ chức đã đủ trưởng thành để sở hữu pipeline, observability, incident response và service contract. Khi đó, microservices có thể là một đánh đổi tốt, không phải vì nó hiện đại, mà vì nó khớp với hình dạng thật của công việc.
Mình từng thấy team bình tĩnh hơn sau khi tách đúng service. Deployment nhỏ hơn. Owner rõ hơn. Dashboard cuối cùng cho một team thấy sức khỏe của thứ họ chịu trách nhiệm. Mình cũng từng thấy team chậm hơn sau khi tách quá sớm. Mỗi feature cần choreography. Mỗi local setup trở nên mong manh. Mỗi bug thành một cuộc điều tra nhỏ xuyên service mà không ai hoàn toàn sở hữu. Khác biệt không nằm ở architecture diagram. Nó nằm ở việc team có đủ lý do và đủ thực hành để trả hóa đơn đó hay chưa.
Chi phí thật sự của microservices không chỉ là cloud spend, Kubernetes YAML hay thêm repository. Nó là sự chú ý cần có để giữ một distributed system vẫn còn hiểu được. Nó là kỷ luật để làm contract rõ ràng, failure nhìn thấy được, dữ liệu có thể sửa lại, và ownership là thật. Nếu team của bạn sẵn sàng trả chi phí đó, microservices có thể tạo ra tự do có ý nghĩa. Nếu chưa, không có gì xấu hổ khi ở lại với một modular monolith được chăm tốt lâu hơn một chút. Kiến trúc tốt không phải thứ nghe có vẻ cao cấp trong cuộc họp. Nó là thứ team có thể vận hành, giải thích và cải thiện vào một chiều thứ Ba rất bình thường.
Nếu bạn từng đi qua một lần migration sang microservices, mình muốn nghe chi phí xuất hiện ở đâu trước với team của bạn: deployment, dữ liệu, debug, testing, hay phần phối hợp lặng lẽ nằm quanh tất cả những điều đó.