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.cs

Mỗ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 IModuleB public 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

DimensionModular MonolithMicroservices
Deployment1 artifactN artifact, N pipeline
Inter-module callIn-process, ~nsNetwork, ~ms
TransactionACID nativeSaga / eventual consistency
Ops complexityThấpCao (service mesh, tracing)
Team fit<50 engineers, domain còn evolveNhiều team autonomous, domain stable
Independent scalingKhông — scale cả unitCó — 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.