← Concepts

阅读中文版本

Modular Monolith

A modular monolith is one deployable with strictly enforced module boundaries and no cross-module data reach-ins, gaining microservice decoupling without the distributed tax.

architecture

Definition

A modular monolith runs as a single process and database while organizing code into modules with enforced boundaries: each module owns its data, exposes an API, and is consumed through that API only. Boundary enforcement can be architectural review, linters, or build-level module separation. The payoff is independent team evolution and a cheap future extraction path.

Context and scope

Chosen by teams wanting domain separation without operational distribution: Shopify, Basecamp-style Rails apps, and many internal platforms. The recommended first shape before microservices.

Common misconceptions

  • A monolith with folders is already modular — modularity requires enforced boundaries; folders without rules decay into a big ball of mud
  • Modules must share one database schema — each module owns its tables; shared schema recreates the coupling modules exist to prevent
  • Microservices later means designing for them now — design clean module boundaries; distribution is an extraction decision made from evidence

Anti-patterns

  • Modules quietly reading each other tables and coupling data models that were meant to evolve separately
  • Skipping boundary enforcement tooling because the team is disciplined — discipline erodes exactly when hiring scales
  • Building a distributed system of two services because microservices, and paying network failure modes for zero scale benefit

Related concepts

Authoritative source

https://martinfowler.com/bliki/MonolithFirst.html

What it is

The useful part of microservices is bounded contexts with explicit contracts; the expensive part is distribution. Modular monoliths keep the first and decline the second.

How it works

Modules encapsulate domain and data. Cross-module access happens through public interfaces — in-process calls that still cross an architectural boundary, enforced by dependency checks in CI. Module-owned database tables get schema access rules. Transactional consistency stays local, which is the single biggest simplification versus distribution.

When to choose

Small-to-medium teams, domains that partition cleanly, and scaling needs satisfied by vertical growth. Extraction paths remain: a module with a hot API can become a service later, with its module API as the seam.

Pitfalls

Boundary erosion is gradual: one convenience import, then a shared table, then the module line is fiction. Keep enforcement automated. In-process calls tempt synchronous chains that must eventually become events for time-decoupling — design the async seams inside modules even in-process.