- Phần lớn monolith không có vấn đề về kích thước — chúng chỉ rối vì thiếu ranh giới.
- Modular Monolith giải quyết bằng module độc lập với public API rõ ràng, cho bạn tính module của microservices mà không phải chịu distributed complexity.
- Đây là cách áp dụng trong .NET năm 2026.
TL;DR
Monolith không phải vấn đề — spaghetti bên trong monolith mới là vấn đề. Cái sai nằm ở cấu trúc, không phải kích thước. Modular Monolith tổ chức code thành các module độc lập, mỗi module có public API riêng và ranh giới được enforce. Bạn giữ nguyên một deployment, một database, không network latency — nhưng vẫn có code rõ ràng, team chạy song song được, và đường chuyển sang microservices sau này là refactor chứ không phải viết lại. Martin Fowler nói rất gọn: "You shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile."
Cốt lõi: ranh giới, không phải kích thước
Một Modular Monolith là một single deployable unit được chia nội bộ thành các module độc lập, loosely coupled. Mỗi module là một bounded context theo ngôn ngữ Domain-Driven Design — nó sở hữu business rules, domain logic, và data access của chính nó. Khác với monolith truyền thống (nơi mọi thứ trộn lẫn thành "big ball of mud"), ranh giới giữa các module ở đây là tường minh và được enforce trong code. Khác với microservices, bạn không phải chịu chi phí của một distributed system: không network serialization, không cascading failure, không Saga transaction.
Vì sao điều này quan trọng trong 2026
Trend microservices đã đi quá đà. Các team nhỏ và mid-size áp dụng microservices quá sớm, rồi phát hiện hóa đơn cloud tăng phi mã (duplicated compute, logging, networking per service), latency chậm đi vì request-to-request qua mạng, và tốc độ dev giảm vì mỗi thay đổi nhỏ phải phối hợp deploy nhiều service. Google công bố paper Towards Modern Development of Cloud Applications liệt kê 5 vấn đề cốt lõi của microservices:
- Performance — overhead của serialization và network
- Correctness — rất khó lý luận về đúng/sai trên hệ phân tán
- Management — mỗi service có release schedule riêng
- Frozen APIs — một khi API ra ngoài rồi, đổi là break consumer
- Development speed — thay đổi nhỏ cần plan deploy nhiều service
Modular Monolith giữ logical boundary của microservices nhưng communication in-process → không vấn đề nào ở trên xuất hiện.
6 lợi ích cụ thể
- Simplified deployment — build/deploy/rollback một đơn vị duy nhất
- In-process performance — gọi hàm, không gọi HTTP/gRPC qua mạng
- Easier transactions — module dùng chung DB, ACID native, không cần Saga
- Dev velocity — một codebase, debug/test dễ, context share được
- Enforced boundaries — dependency chỉ flow inward, cross-module qua public API
- Path to microservices — khi cần tách module ra service, đó là refactor chứ không phải rewrite
Áp dụng trong .NET: hai cấp kiến trúc
Milan Jovanovic chia quyết định thiết kế thành hai cấp:
Macro architecture — chia hệ thống thành module
Mỗi module = một bounded context. Quyết định: ranh giới module, communication pattern (sync qua public API vs async qua domain events), data isolation (không share database giữa các domain), public API design. Data isolation là điều kiện bắt buộc — nếu hai module query chung bảng, ranh giới đã vỡ.
Micro architecture — tổ chức code bên trong module
Ông recommend Vertical Slice Architecture (VSA) hoặc Pragmatic Clean Architecture, và có thể mix-and-match tùy module. Một module Ticketing với vertical slice thường trông như:
Modules/
Ticketing/
Features/
AddItemToCart/AddItemToCart.cs
SubmitOrder/SubmitOrder.cs
GetOrder/GetOrder.cs
CancelOrder/CancelOrder.cs
Data/TicketingDbContext.cs
Entities/Order.cs
ITicketingModule.cs
TicketingModule.csMỗi file feature là một static class gói Request, Response, Validator, Handler, Endpoint trong một chỗ — phẳng, scannable, thêm feature = thêm folder. Khi slice phức tạp lên, push logic vào rich domain model (entities, value objects, domain events) — đúng tinh thần DDD.
Enforce ranh giới trong code
Ranh giới không phải là thứ "giả định" — nó phải được kiểm tra tự động. Trong .NET, viết Architecture Tests (dùng NetArchTest hoặc ArchUnitNET) để verify:
- Module A không được reference trực tiếp internal types của Module B — chỉ qua
IModuleBpublic interface - Dependencies flow inward (Domain không biết Infrastructure)
- Không có cross-module DB query
Cross-module communication thì dùng Domain Events Dispatcher custom để mô phỏng message bus in-process. Khi tách module ra service, swap dispatcher sang RabbitMQ/Kafka là xong.
Modular Monolith vs Microservices
| Dimension | Modular Monolith | Microservices |
|---|---|---|
| Deployment | 1 artifact | N artifact, N pipeline |
| Inter-module call | In-process, ~ns | Network, ~ms |
| Transaction | ACID native | Saga / eventual consistency |
| Ops complexity | Thấp | Cao (service mesh, tracing) |
| Team fit | <50 engineers, domain còn evolve | Nhiều team autonomous, domain stable |
| Independent scaling | Không — scale cả unit | Có — scale từng service |
Khi nào chọn Modular Monolith
- Startup hoặc mid-size team cần speed và adaptability
- SaaS đang tăng trưởng nhưng domain còn đang định hình
- Team <50 engineer — Thoughtworks nghiên cứu: coordination overhead của microservices > independence gain
- Đang migrate ngược từ microservices về vì cloud cost + latency
Microservices vẫn đúng khi: nhiều team autonomous, domain stable, independent scaling là hard requirement, hoặc cần tech stack đa dạng per service. Như Simon Brown: "Choose microservices for the benefits, not because your monolithic codebase is a mess."
Pitfalls cần tránh
- Ranh giới chỉ "giả định" — không có Architecture Tests = code sẽ slow leak thành mud lại
- Module chatty — hai module gọi nhau liên tục là dấu hiệu ranh giới vẽ sai; merge lại
- Shared DB across domains — ruin future extraction, biến refactor thành rewrite
- Mỗi module dùng chung technical layering — không cần, mỗi module có thể chọn internal architecture phù hợp context của nó
Kết: architecture là context, không phải ideology
Insight lớn nhất của 2026: architecture nên theo mức độ trưởng thành của team và domain, không theo fashion. Microservices là đích đến, không phải điểm xuất phát. Bắt đầu bằng modular monolith cho bạn tất cả lợi ích của modularity — team chạy song song, code dễ reason về, boundaries enforced by design — mà không phải trả giá distributed complexity từ ngày 1. Khi một module thực sự cần scale độc lập hoặc team tăng lên, bạn tách nó ra thành service. Đó là một quyết định có dữ liệu, không phải một cú bet upfront.
Nguồn: Milan Jovanovic — What Is a Modular Monolith, Vertical Slices inside Modular Monolith, Rethinking Microservices in 2026.
