Architecture & System Design › Architecture Styles
Modular Monolith
A monolith with strong internal module boundaries.
Also known as: modulith, modular monolith architecture, well-structured monolith, monolith with modules
A modular monolith is an application deployed as one unit, but internally organized into well-separated modules with strict boundaries, each owning a business capability (orders, billing, catalog, users). It aims to get the simplicity of a monolith (one deployment, in-process calls, easy transactions and debugging) plus the discipline of microservices (clear ownership and boundaries).
one deployable
├── orders/ public API: OrdersFacade ─ internal code and tables hidden
├── billing/ public API: BillingFacade
├── catalog/ public API: CatalogFacade
└── users/ public API: UsersFacade
modules talk through public interfaces or events, never through each other's internals or tables
What makes it “modular”
- Modules map to business domains, with high cohesion inside and low coupling between (cohesion, coupling).
- Explicit public interfaces. Other modules may call only those. Everything else is private.
- Data ownership: each module owns its tables, and other modules don’t query them directly. They go through the module’s interface (or consume its events). This is the boundary that matters most.
- Enforced boundaries: not just conventions. Use language visibility, package or module systems, and tooling that fails the build on forbidden dependencies (architecture tests, dependency rules).
- No circular dependencies between modules.
- Often combined with ideas from hexagonal architecture inside each module.
Why it’s attractive
- Simple operations: one deployment, one database, one monitoring setup.
- Easy refactoring and transactions across modules, in-process calls with no network failures.
- Easier local development and testing.
- A path forward: well-defined modules with owned data can later be extracted into services if a real need appears (independent scaling, separate team), at lower risk than splitting a tangled codebase.
- Avoids the distributed-systems tax until you need it (microservices, distributed monolith).
The risks
- Boundaries erode without enforcement. A “modular” monolith where anyone can import anything and join any table is just a big ball of mud with folders.
- Shared database temptation: cross-module joins are easy and tie everything together.
- One deploy unit: a single team’s bug can break the release for everyone. Independent scaling and independent releases aren’t available.
- Getting module boundaries wrong is costly either way. Learn the domain, and start with coarse modules.
- Build times and repository size can grow, though tooling helps.
When to choose it
For most products and most team sizes, it’s a strong default: start modular, stay monolithic until a concrete reason forces a split. See monolith vs microservices for the decision.