Sự Oan Khuất Của Monolith: Tội Đồ Hay Lựa Chọn Hoàn Hảo Cho 90% Dự Án?
Lướt các diễn đàn công nghệ hiện nay, cứ 10 bài viết thì 9 bài ca ngợi Microservices. Khái niệm Monolith (Kiến trúc nguyên khối) thường xuyên bị lôi ra làm "tội đồ", bị gắn mác là lỗi thời, là cục nợ kỹ thuật (technical debt). Rất nhiều anh em dev trẻ vừa khởi tạo dự án đã vội vàng đòi chia nhỏ 5-7 services cho "chuẩn trend".
Nhưng dưới góc nhìn của những kỹ sư từng trải, những người phải gánh chịu hậu quả của việc "over-engineering" (thiết kế quá đà), Monolith lại mang một vị thế hoàn toàn khác. Hôm nay, chúng ta sẽ trả lại sự công bằng cho Monolith, mổ xẻ xem nó thực chất là gì và tại sao nó vẫn là chân ái của 90% dự án.
Lời mở đầu: Bệnh "cuồng" Microservices
Bạn nhận một dự án mới. Sếp vỗ vai: "Hệ thống này tương lai sẽ phục vụ hàng triệu user, em thiết kế sao cho xịn nhé". Thế là bạn vẽ ra một sơ đồ hoành tráng với hàng chục ô vuông Microservices, kết nối bằng Message Broker, chạy trên Kubernetes.
Sáu tháng sau, dự án... phá sản vì mới làm xong phần hạ tầng (infrastructure), chưa có tính năng nào ra hồn để user dùng thử. Trong khi đó, đối thủ dùng một con app Laravel cục mịch, đẩy code lên con VPS 20 đô/tháng, chạy tành tành lấy hết thị phần.
Đó là minh chứng rõ nhất cho câu nói kinh điển của Martin Fowler: "Đừng bao giờ bắt đầu với Microservices. Hãy bắt đầu bằng Monolith".
Vậy rốt cuộc, Monolith là cái quái gì mà lại bị ghét, rồi lại được khuyên dùng?
1. Monolith thực chất là gì?
Monolith (Kiến trúc nguyên khối) là một mô hình thiết kế phần mềm mà ở đó: Toàn bộ ứng dụng được xây dựng như một khối thống nhất duy nhất.
Nghĩa là:
- Một Codebase: Tất cả các module (Quản lý User, Quản lý Đơn hàng, Thanh toán, Báo cáo...) nằm chung trong một repository.
- Một Process (Tiến trình): Khi chạy ứng dụng, nó chạy dưới dạng một tiến trình duy nhất trên server.
- Một Deployment Unit: Mỗi lần muốn đưa tính năng mới lên Production, bạn phải đóng gói (build) và triển khai (deploy) toàn bộ cái mớ khổng lồ đó, dù bạn chỉ vừa sửa lỗi sai chính tả ở một dòng text.
- Một Database (Thường là vậy): Các module thoải mái query chéo sang các bảng của nhau.
Nghe rất đơn giản và quen thuộc đúng không? Bất kỳ ai từng gõ lệnh laravel new hoặc tạo một project Spring Boot cơ bản đều đang xây dựng một ứng dụng Monolith.
2. Nỗi đau mang tên "Cục bùn khổng lồ" (Big Ball of Mud)
Monolith bản chất không xấu, nó chỉ trở nên tồi tệ khi hệ thống phình to mà không được kiểm soát kỷ luật. Hãy xem những "vết thương chí mạng" khiến anh em Backend phải khóc thét khi ôm một con Monolith lâu năm:
A. Sự ràng buộc tử thần (Tight Coupling)
Do xài chung một database và nằm chung một source code, anh dev làm module Báo Cáo tiện tay viết luôn một câu JOIN thẳng vào bảng orders và users của module Thanh Toán.
Một ngày đẹp trời, team Thanh Toán cần đổi tên cột hoặc đổi logic sinh mã đơn hàng. Bùm! Toàn bộ tính năng Báo Cáo lăn ra chết. Mọi thứ dính chặt vào nhau như một đĩa mì Spaghetti.
B. Nỗi ám ảnh "Deploy" Khi hệ thống có 50 dev cùng code chung một cái Monolith, việc deploy là một canh bạc. Bạn vừa fix xong một bug nhỏ ở tính năng Gửi Email, nhưng khi gộp code (merge), một anh dev khác lại vô tình đẩy lên một đoạn code gây lỗi vòng lặp vô hạn ở module Thanh Toán. Khi đưa cục Monolith lên Production, tính năng Thanh Toán sập, kéo theo tính năng Gửi Email của bạn cũng "tắt điện" vì chúng nằm chung một Process. Một module sập = Cả hệ thống sập.
C. Nút thắt cổ chai về Scale (Mở rộng) Giả sử hệ thống của bạn có tính năng Đọc bài viết (traffic cực cao, cần nhiều RAM) và tính năng Render Video (traffic thấp nhưng ngốn cực nhiều CPU). Với Monolith, bạn không thể chỉ cấp thêm RAM cho chức năng Đọc bài viết. Bạn bắt buộc phải nhân bản TOÀN BỘ ứng dụng khổng lồ đó ra thành nhiều server. Việc này cực kỳ lãng phí tài nguyên và kém hiệu quả.
3. Nếu tệ như vậy, tại sao vẫn khuyên dùng Monolith? Vì Microservices là một giải pháp hạng sang dành cho những vấn đề hạng sang (Premium solution for premium problems). Khi chia nhỏ hệ thống, bạn phải đối mặt với độ trễ mạng (network latency), giao dịch phân tán (distributed transactions), và cơn ác mộng về quản lý hạ tầng.
Với 90% dự án ngoài kia (kể cả những hệ thống có vài chục ngàn truy cập một ngày), bạn không cần đến Microservices. Cái bạn cần là Modular Monolith (Nguyên khối phân hệ).
Kiến trúc Modular Monolith vươn lên như một đấng cứu thế:
- Vẫn là một codebase, một lần deploy (giữ được tốc độ phát triển cực nhanh của Monolith).
- NHƯNG các module (User, Order, Payment) được chia thành các thư mục độc lập.
- Luật thép: Module này KHÔNG ĐƯỢC PHÉP query thẳng vào database của module kia. Chúng phải giao tiếp với nhau qua các hàm (Interfaces/Contracts) được định nghĩa sẵn, y hệt như gọi API.
Đến khi dự án thực sự bùng nổ, việc tách một module từ Modular Monolith ra thành một Microservice độc lập chỉ là câu chuyện cắt/dán code và đổi hàm gọi sang giao thức HTTP/gRPC.
Tóm lại
Đừng phán xét Monolith. Lỗi không nằm ở kiến trúc nguyên khối, lỗi nằm ở những kỹ sư thiếu kỷ luật đã biến nó thành một mớ hỗn độn.
- Nếu team bạn dưới 10 người, nghiệp vụ chưa rõ ràng, cần ra mắt sản phẩm nhanh: Chắc chắn phải chọn Monolith.
- Thay vì ảo tưởng về Microservices, hãy học cách viết Modular Monolith - nơi ranh giới giữa các nghiệp vụ (Domain boundaries) được phân định rõ ràng ngay từ ngày đầu tiên.
Kiến trúc tốt là kiến trúc tiến hóa được theo thời gian, chứ không phải kiến trúc phức tạp nhất. Chúc anh em chọn đúng "vũ khí" cho dự án của mình!
All rights reserved