0

[Series System Design - Bài 4] Monolith vs. Microservices: Cuộc chiến không hồi kết. Khi nào thì nên chia nhỏ, khi nào thì nên giữ nguyên?

Chào anh em, nếu dạo quanh các diễn đàn công nghệ hay các nhóm chat của developer, anh em sẽ rất dễ bắt gặp một hội chứng tạm gọi là "Cuồng Microservices".

Cứ hễ start một project mới, dù chỉ là cái web bán hàng nội bộ có vỏn vẹn 100 users, nhiều anh em đã vội vã lôi Docker, Kubernetes, Kafka ra setup thành chục cái services khác nhau cho... "đúng chuẩn thời đại". Để rồi vài tháng sau, chính họ chìm nghỉm trong đống log phân tán và những tiếng khóc thét khi network timeout.

Là một người từng đập đi xây lại không biết bao nhiêu hệ thống trong suốt 20 năm qua, tôi muốn chia sẻ với anh em một góc nhìn trần trụi hơn về cuộc chiến giữa Monolith (Nguyên khối) và Microservices (Vi dịch vụ).

1. Monolith: Gã khổng lồ cục mịch nhưng đáng tin cậy

Monolith đơn giản là bạn tống tất cả mọi thứ: User, Product, Order, Payment... vào chung một project (ví dụ: một cục source code Laravel siêu to khổng lồ), dùng chung một Database và deploy lên chung một (hoặc một cụm) Server.

Tại sao chúng ta hay "khinh" Monolith? Bởi vì khi hệ thống lớn lên, nó biến thành một đĩa mỳ Ý (Spaghetti code) đúng nghĩa. Một ông dev mới vào sửa logic tính tiền khuyến mãi, vô tình làm chết luôn cả module đăng nhập. Thời gian build và deploy lâu khủng khiếp. Khi bị nghẽn ở module Thanh toán, bạn phải scale "mù" cả một cục to đùng, cực kỳ lãng phí tài nguyên RAM và CPU.

Nhưng... Monolith có thực sự tệ? Không hề! Monolith là kiến trúc hoàn hảo nhất để bắt đầu. Nó cho phép team bạn di chuyển cực nhanh để chứng minh mô hình kinh doanh (Product-Market Fit). Việc test dễ dàng, debug mượt mà vì mọi function call đều nằm trong cùng một bộ nhớ (memory). Không có độ trễ mạng (network latency), không có bài toán Eventual Consistency đau đầu.

2. Microservices: Nữ hoàng kiêu kỳ của thời đại Cloud

Microservices là việc bạn "chặt" cục Monolith kia ra thành nhiều services nhỏ, hoạt động độc lập. Tức là Service Quản lý Đơn hàng sẽ chạy một server riêng, có database riêng, và Service Kho hàng cũng vậy. Chúng nói chuyện với nhau qua API hoặc Message Queue.

Sức mạnh của Microservices: Hãy hình dung backend của một hệ thống bán lẻ mỹ phẩm khổng lồ hoặc chuỗi siêu thị. Ngày Flash Sale, traffic vào xem sản phẩm (Product) và chốt đơn (Order) tăng gấp 100 lần, trong khi module Quản lý Nhân sự (HR) chả có ma nào vào. Với Microservices, bạn chỉ cần scale đúng cụm Product và Order. Hơn nữa, nếu team Order thích viết bằng Go cho nhanh, team HR muốn xài PHP/Laravel cho dễ bảo trì, chuyện đó hoàn toàn khả thi (Technology Agnostic).

Nhưng cái giá phải trả là sự phức tạp tột cùng: Bạn vừa giải quyết được vấn đề về "Code", thì bạn lại rước vào thân vấn đề về "Mạng" và "Vận hành".

  • Làm sao để đảm bảo dữ liệu đồng bộ khi Service Order báo thành công nhưng Service Payment lại báo lỗi kết nối?
  • Debug một request đi xuyên qua 5 services khác nhau như thế nào?
  • Chi phí duy trì hạ tầng (DevOps, Monitor, CI/CD) tăng theo cấp số nhân.

3. Khi nào chia nhỏ? Khi nào giữ nguyên?

Đây là câu hỏi ăn tiền nhất của một System Designer. Đừng bao giờ chọn Microservices chỉ vì Netflix hay Amazon đang dùng nó. Hãy trả lời 3 câu hỏi sau:

  • Quy mô team của bạn lớn cỡ nào? Định luật Conway nói rằng: Kiến trúc hệ thống phản ánh cấu trúc giao tiếp của tổ chức. Nếu team bạn chỉ có 5-10 backend dev, đừng chia Microservices. Bạn sẽ tốn thời gian maintain hạ tầng nhiều hơn là code tính năng. Hãy giữ một kiến trúc Modular Monolith (Monolith nhưng code được phân chia module rõ ràng). Chỉ chia khi bạn có từ 3-4 team độc lập, cần deploy riêng biệt mà không muốn giẫm chân lên nhau.
  • Domain (Nghiệp vụ) của bạn có thực sự cần tách biệt? Hãy nhìn vào hệ thống thu phí tự động (AFC) của tuyến Metro. Module xử lý giao dịch tại cổng soát vé (Gate) đòi hỏi phản hồi mili-giây, trong khi module đối soát doanh thu cuối ngày có thể chạy chậm rãi. Hai đặc tính tải quá khác biệt này là một lý do chính đáng để tách service.
  • Bạn đã hiểu rõ Domain chưa? Nếu nghiệp vụ còn đang thay đổi xoành xoạch, việc chia tách sai ranh giới (Boundaries) sẽ biến hệ thống của bạn thành "Distributed Monolith" (Nguyên khối phân tán) - dạng tồi tệ nhất của kiến trúc, nơi bạn vừa phải chịu sự cồng kềnh của Monolith, vừa gánh độ trễ mạng của Microservices.

Lời kết

Bắt đầu bằng một Majestic Monolith (Một khối nguyên khối tráng lệ và gọn gàng). Chỉ bẻ nhỏ nó ra khi những "nỗi đau" về việc scale up và quản lý team vượt qua "nỗi đau" của việc vận hành hệ thống phân tán. Đừng biến Microservices thành một quả bom nổ chậm do thiếu năng lực vận hành.

Nhưng khoan đã, dù bạn viết Monolith hay Microservices, có một thứ nếu bạn không bảo vệ chặt chẽ, code của bạn sẽ thối rữa từ bên trong: Đó chính là Core Business Logic (Logic nghiệp vụ cốt lõi). Sự bùng nổ của các framework hiện đại đôi khi làm chúng ta quên mất điều này.

👉 Ở bài tiếp theo, chúng ta sẽ đi sâu vào "trái tim" của mã nguồn: "Hexagonal Architecture (Kiến trúc lục giác) & Domain-Driven Design (DDD): Cách bảo vệ 'core logic' của ứng dụng khỏi sự thay đổi của Framework và Database."

Một chủ đề cực kỳ nặng đô nhưng sẽ thay đổi hoàn toàn tư duy code của anh em. Nhớ follow và chia sẻ bài viết để đón xem phần tiếp theo nhé! Nếu team anh em đang xài Microservices, anh em thấy nó là "chân ái" hay là "của nợ", comment bên dưới nhé!


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.