Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 51 — Microservices vs Monolith Trade-offs

Technical Product Manager Bài 51/60

Mở đầu — vì sao bài này quan trọng

Nếu bạn làm Technical PM đủ lâu, sớm muộn gì bạn cũng ngồi trong một cuộc họp mà ở đó câu hỏi lớn được đặt ra: "Chúng ta nên tách hệ thống này thành microservices, hay giữ nguyên monolith?" Và bạn sẽ ngạc nhiên khi thấy đây không chỉ là quyết định kỹ thuật của engineering — nó là quyết định ảnh hưởng trực tiếp đến tốc độ ra tính năng, chi phí vận hành, cấu trúc team, và thậm chí cả lộ trình tuyển dụng của công ty.

Là PM, bạn không cần viết code để tách service. Nhưng bạn cần hiểu đủ sâu để: (1) nhận ra khi nào engineering đang "over-engineer" — chia nhỏ quá sớm khi sản phẩm chưa cần; (2) nhận ra khi nào monolith đang trở thành nút thắt cổ chai khiến team không thể ship nhanh; và (3) dịch được trade-off kỹ thuật thành ngôn ngữ business để bảo vệ quyết định với ban lãnh đạo.

Đây là một trong những quyết định kiến trúc tốn kém nhất nếu làm sai. Tách quá sớm, bạn đốt 6 tháng engineering để xây hạ tầng phân tán mà lẽ ra nên dùng để xây tính năng cho khách hàng. Giữ monolith quá lâu, mỗi lần deploy trở thành cơn ác mộng và một team chặn đường ba team khác. Bài này giúp bạn cầm được cán cân đó.

Khái niệm cốt lõi

Monolith là gì

Monolith (kiến trúc khối) là khi toàn bộ ứng dụng được đóng gói thành một codebase duy nhất, build và deploy như một đơn vị. Tính năng đăng nhập, giỏ hàng, thanh toán, gửi email... tất cả nằm chung một dự án, chạy chung một process, thường kết nối tới một database.

Điểm mạnh:

  • Đơn giản để bắt đầu. Một repo, một pipeline deploy, một nơi để chạy local. Lập trình viên mới onboard nhanh.
  • Dễ debug và trace. Một request đi xuyên suốt trong cùng một process, bạn đọc stack trace là thấy toàn bộ đường đi. Không có chuyện request "biến mất" giữa các service.
  • Giao dịch (transaction) dễ đảm bảo. Vì dùng chung một database, bạn có thể bọc nhiều thao tác trong một transaction ACID — hoặc tất cả thành công, hoặc tất cả rollback.
  • Phát triển nhanh ở giai đoạn đầu. Không tốn công xây hạ tầng giao tiếp giữa các service.
Điểm yếu:
  • Khó scale từng phần. Nếu chỉ riêng module xử lý ảnh ngốn CPU, bạn vẫn phải nhân bản toàn bộ ứng dụng để scale, lãng phí tài nguyên.
  • Deploy là tất-cả-hoặc-không-gì. Sửa một dòng ở module báo cáo cũng buộc deploy lại cả hệ thống. Rủi ro mỗi lần release cao hơn.
  • Coupling tăng theo thời gian. Khi codebase phình to, các module bám dính vào nhau, sửa chỗ này vỡ chỗ kia. Người ta gọi là "big ball of mud" (cục bùn lớn).
  • Nhiều team giẫm chân nhau. 50 engineer commit chung một repo thì merge conflict và chờ nhau review trở thành thường nhật.

Microservices là gì

Microservices là kiến trúc trong đó ứng dụng được chia thành nhiều service nhỏ, độc lập, mỗi service phụ trách một năng lực nghiệp vụ (business capability) riêng, có thể có database riêng, deploy riêng, và giao tiếp với nhau qua mạng (thường là REST, gRPC, hoặc message queue).

Điểm mạnh:

  • Scale độc lập. Service tìm kiếm bị tải nặng thì chỉ scale riêng nó, không động đến phần còn lại.
  • Deploy độc lập. Team thanh toán ship phiên bản mới mà không cần đợi team profile. Tốc độ release theo team tăng lên.
  • Cô lập lỗi (fault isolation). Một service sập không nhất thiết kéo sập cả hệ thống nếu bạn thiết kế đúng (timeout, fallback, circuit breaker).
  • Tự do công nghệ. Mỗi team có thể chọn ngôn ngữ/stack phù hợp cho service của mình (dù tự do này nên được kiểm soát).
  • Phù hợp tổ chức lớn. Theo định luật Conway, cấu trúc hệ thống thường phản chiếu cấu trúc team. Microservices cho phép nhiều team tự chủ song song.
Điểm yếu:
  • Phức tạp vận hành khổng lồ. Bạn phải lo về service discovery, load balancing, monitoring phân tán, tracing xuyên service, retry, timeout. Cái mà trong monolith chỉ là một lệnh gọi hàm, nay là một cuộc gọi mạng có thể thất bại.
  • Giao dịch phân tán khó. Không còn transaction ACID xuyên service. Bạn phải dùng các pattern như saga, eventual consistency — phức tạp và dễ sai.
  • Chi phí hạ tầng và con người cao hơn. Cần DevOps/Platform engineering mạnh, cần CI/CD trưởng thành, cần văn hóa observability.
  • Debug khó hơn. Một request đi qua 7 service, lỗi nằm ở đâu? Bạn cần distributed tracing mới lần ra.

Trục quyết định thật sự: không phải "cái nào tốt hơn"

Sai lầm phổ biến nhất của PM là hỏi "microservices hay monolith tốt hơn?". Câu hỏi đúng là: "Ở giai đoạn và quy mô hiện tại của chúng ta, kiến trúc nào giảm thiểu tổng chi phí — gồm chi phí phát triển, vận hành, và cơ hội?"

Các trục cần cân:

  • Quy mô team. Dưới 10-15 engineer thường monolith vẫn ổn. Trên ngưỡng đó, việc phối hợp trong một codebase bắt đầu đắt.
  • Độ trưởng thành DevOps. Không có CI/CD, observability, automation tốt thì microservices sẽ là địa ngục vận hành.
  • Tốc độ thay đổi từng phần. Nếu một số module thay đổi liên tục còn số khác gần như đứng yên, đó là tín hiệu nên tách phần "nóng" ra.
  • Yêu cầu scale khác biệt. Nếu các phần có hồ sơ tải rất khác nhau (ví dụ search ngốn CPU, còn checkout thì ít request nhưng cần độ tin cậy cao), tách giúp tối ưu tài nguyên.
Một lựa chọn trung gian rất đáng giá mà PM nên biết: modular monolith — vẫn một deploy, nhưng bên trong code được chia module rạch ròi với ranh giới rõ ràng. Nó giữ sự đơn giản của monolith nhưng chuẩn bị sẵn đường để tách service sau này khi thật sự cần.

Tình huống thực tế

Ví dụ 1 — Sàn TMĐT Việt Nam tách quá sớm

Một startup TMĐT tại TP.HCM (gọi là "ShopViet") gọi vốn vòng seed 1,5 triệu USD. Đội kỹ thuật chỉ có 8 engineer. CTO mới về từ một big tech, mê microservices, quyết định ngay từ đầu chia hệ thống thành 14 service: user, product, cart, order, payment, inventory, notification, search, review...

Sau 5 tháng, kết quả: họ tốn gần như toàn bộ thời gian để xây và vá hạ tầng — API gateway, service mesh, hệ thống log tập trung, xử lý lỗi giao tiếp giữa service. Một bug "đặt hàng nhưng không trừ kho" mất ba ngày để debug vì phải lần qua bốn service và một message queue. Trong khi đó, đối thủ ra mắt tính năng flash sale trước họ hai tháng.

Diễn giải: với 8 người, mỗi service trung bình chưa tới một người phụ trách. Lợi ích lớn nhất của microservices — cho phép nhiều team làm việc độc lập — gần như bằng không vì họ không có nhiều team. Còn cái giá — độ phức tạp phân tán — thì họ trả đủ.

Bài học: Microservices giải bài toán tổ chức nhiều hơn bài toán kỹ thuật. Khi bạn chưa có vấn đề tổ chức (quá nhiều team giẫm chân nhau), tách service sớm chỉ mua thêm rắc rối. Một modular monolith hẳn đã giúp ShopViet ship nhanh gấp đôi.

Ví dụ 2 — Tiki và áp lực tách theo quy mô

Tiki, một trong những sàn TMĐT lớn của Việt Nam, khi đạt quy mô hàng trăm engineer và lưu lượng đỉnh điểm vào các đợt sale lớn, đối mặt với bài toán ngược lại. Mô hình monolith ban đầu (hoặc một vài service lớn) không còn đáp ứng được khi: module tìm kiếm và gợi ý sản phẩm cần scale rất mạnh vào giờ cao điểm, trong khi module quản lý nhà bán hàng thì tải ổn định; và hàng chục team cần ship độc lập mỗi ngày.

Ở quy mô này, việc tách các năng lực như search, recommendation, pricing, order management thành các service riêng cho phép: scale riêng phần "nóng" mà không nhân bản toàn hệ thống (tiết kiệm chi phí hạ tầng đáng kể vào mùa sale), và mỗi team sở hữu trọn vòng đời service của mình.

Diễn giải: đây là điểm mà cán cân đảo chiều. Khi tổ chức đủ lớn và yêu cầu scale đủ khác biệt, chi phí phối hợp trong một khối lớn vượt quá chi phí vận hành phân tán. Lúc này microservices không còn là "vẽ vời" mà là nhu cầu thật.

Bài học: Kiến trúc đúng thay đổi theo giai đoạn. Cùng một công ty, monolith đúng ở năm thứ nhất có thể sai ở năm thứ năm. PM cần nhận ra tín hiệu chuyển pha, đừng bám vào quyết định cũ chỉ vì "nó từng đúng".

Ví dụ 3 — Hành trình của Amazon và bài học về "tách theo nỗi đau"

Amazon là ví dụ kinh điển. Ban đầu họ là một monolith lớn. Khi tổ chức phình ra hàng nghìn engineer, mỗi lần deploy trở thành sự kiện tốn hàng giờ phối hợp, và team này thường xuyên chặn đường team kia. Họ chuyển dần sang kiến trúc service-oriented, gắn với mô hình "two-pizza team" — mỗi team nhỏ đến mức hai chiếc pizza đủ cho cả nhóm — và mỗi team sở hữu service của mình end-to-end.

Điểm đáng học không phải là "Amazon dùng microservices nên ta cũng nên dùng", mà là họ tách dần dần theo nỗi đau thực tế, không tách một lần cho oai. Họ xác định ranh giới service quanh năng lực nghiệp vụ, và chỉ tách khi monolith thật sự cản trở tốc độ.

Bài học: chiến lược an toàn nhất thường là bắt đầu bằng monolith (hoặc modular monolith), rồi tách dần những phần chứng minh được nhu cầu. Người ta hay gọi đây là "monolith first". Tách phần nào đang đau nhất trước — chứ không phải vẽ sơ đồ 20 service rồi đi xây tất cả.

Hướng dẫn từng bước

Khi đứng trước quyết định này, đây là khung mà PM có thể dẫn dắt cùng tech lead:

  • Xác định bạn đang giải bài toán gì. Viết ra cụ thể nỗi đau: "deploy chậm và rủi ro", "team A chặn team B", "module X cần scale gấp 10 lần phần còn lại", hay "muốn dùng công nghệ khác cho phần Y". Nếu không nêu được nỗi đau cụ thể, có thể bạn chưa cần tách gì cả.
  • Đánh giá độ sẵn sàng vận hành. Hỏi thẳng: chúng ta đã có CI/CD tự động chưa? Có monitoring và distributed tracing chưa? Có người chuyên về platform/DevOps không? Nếu phần lớn là "chưa", microservices sẽ khuếch đại điểm yếu này lên gấp bội.
  • Vẽ ranh giới theo năng lực nghiệp vụ, không theo tầng kỹ thuật. Service đúng nên bao quanh một domain (ví dụ "Thanh toán", "Quản lý kho") chứ không phải một tầng kỹ thuật (đừng có service "Database" hay service "Controller"). Đây là nơi PM đóng góp nhiều nhất vì bạn hiểu domain.
  • Cân nhắc modular monolith như bước đệm. Nếu nỗi đau chính là coupling code chứ chưa phải scale hay tổ chức, hãy đề xuất chia module rõ ràng trong cùng một deploy trước. Rẻ hơn, an toàn hơn, và mở đường tách sau.
  • Nếu tách, tách từng phần và đo lường. Chọn một service ít rủi ro nhưng có lợi ích rõ (ví dụ notification) để tách thử. Đo: tốc độ deploy, số sự cố, chi phí hạ tầng trước và sau. Lấy dữ liệu thật làm cơ sở cho các bước tách tiếp theo.
  • Lập ngân sách cho cái giá ẩn. Dự trù thời gian và người cho observability, on-call, xử lý lỗi phân tán. Đưa các chi phí này vào roadmap minh bạch, đừng để chúng âm thầm ăn mất velocity rồi cả team ngạc nhiên không hiểu sao chậm.

Lỗi thường gặp & mẹo

  • Tách microservices vì "thấy ngầu", không vì nhu cầu. Đây là lỗi đắt nhất. Mẹo: trước mỗi đề xuất tách, bắt buộc viết một câu "Chúng ta tách service này vì... và nếu không tách thì hậu quả cụ thể là...". Nếu không điền nổi, dừng lại.
  • Nhầm microservices với "service nhỏ về mặt code". Nhỏ không phải mục tiêu. Một service đúng được định nghĩa bởi ranh giới nghiệp vụ độc lập, không phải số dòng code. Chia quá vụn tạo ra "nanoservices" với chi phí giao tiếp khổng lồ.
  • Bỏ qua giao dịch phân tán. PM thường quên rằng trong monolith "đặt hàng và trừ kho" là một transaction an toàn, còn khi tách ra hai service thì cần saga và chấp nhận eventual consistency. Hãy hỏi tech lead "dữ liệu có thể không nhất quán tạm thời ở đâu, và điều đó ảnh hưởng gì tới trải nghiệm khách hàng?".
  • Chia sẻ chung một database giữa các service. Đây là cái bẫy phổ biến: tách code nhưng vẫn dùng chung DB. Kết quả là bạn có tất cả nhược điểm của phân tán mà không có ưu điểm độc lập. Mỗi service nên sở hữu dữ liệu của mình.
  • Quên định luật Conway. Nếu cấu trúc team không khớp với ranh giới service mong muốn, kiến trúc sẽ bị kéo lệch. Mẹo: thiết kế ranh giới service và ranh giới team cùng lúc.
  • Mẹo cho PM: Hãy là người bảo vệ sự đơn giản. Trong phần lớn trường hợp, nghi ngờ mặc định nên nghiêng về "giữ đơn giản hơn" — vì độ phức tạp phân tán hầu như không bao giờ được đánh giá đủ cao, còn lợi ích thì hay bị thổi phồng.

Bài tập thực hành

  • Phân tích một quyết định. Chọn một sản phẩm bạn biết (nơi bạn làm hoặc một startup quen thuộc). Viết một đoạn nửa trang trả lời: hiện tại họ nên dùng monolith, modular monolith, hay microservices? Nêu ít nhất ba lý do dựa trên quy mô team, độ trưởng thành DevOps và yêu cầu scale.
  • Vẽ ranh giới service. Cho một ứng dụng giao đồ ăn giả định (đặt món, thanh toán, theo dõi tài xế, đánh giá, khuyến mãi). Hãy đề xuất cách chia thành 4-6 service theo năng lực nghiệp vụ. Với mỗi cặp service phải giao tiếp, ghi chú dữ liệu nào có thể không nhất quán tạm thời và rủi ro với khách hàng.
  • Viết luận điểm cho lãnh đạo. Giả sử tech lead đề xuất tách monolith hiện tại thành 12 service trong khi team chỉ có 9 người. Viết một email ngắn (200 từ) dưới góc nhìn PM, đặt câu hỏi đúng và đề xuất một lộ trình thay thế an toàn hơn, kèm cách đo lường để quyết định bước tiếp theo.

Tóm tắt

  • Microservices và monolith không có cái nào "tốt hơn" tuyệt đối — câu hỏi đúng là kiến trúc nào giảm tổng chi phí ở giai đoạn và quy mô hiện tại của bạn.
  • Monolith thắng ở sự đơn giản, dễ debug, transaction an toàn và tốc độ phát triển ban đầu — lý tưởng cho team nhỏ và sản phẩm còn đang tìm product-market fit.
  • Microservices thắng khi tổ chức lớn cần nhiều team tự chủ, khi các phần có nhu cầu scale rất khác nhau, và khi bạn đã có nền tảng DevOps/observability trưởng thành — nhưng phải trả giá bằng độ phức tạp phân tán đáng kể.
  • Modular monolith là điểm trung gian quý giá: giữ sự đơn giản mà vẫn chuẩn bị đường tách sau.
  • Chiến lược an toàn nhất thường là "monolith first": bắt đầu đơn giản, rồi tách dần theo nỗi đau thực tế đã được chứng minh bằng dữ liệu.
  • Vai trò của PM: bảo vệ sự đơn giản, đòi hỏi lý do cụ thể trước mỗi lần tách, vẽ ranh giới theo domain, và dịch trade-off kỹ thuật thành ngôn ngữ business cho lãnh đạo.