pattern

Modular Monolith Boundaries

A single deployable unit with strictly enforced internal module boundaries, giving domain separation without distribution.

monolithmodularityarchitecture

The pattern has become the sensible default for new systems because it separates two things that microservices bundle together: logical modularity, which is nearly always valuable, and physical distribution, which is expensive and only sometimes necessary.

A modular monolith keeps one deployable and one database, and enforces module boundaries in code: each module owns its data and exposes an explicit interface, other modules call only through that interface, and cross-module database access is prohibited. Crucially, the enforcement is automated — an architecture test or compiler-level boundary that fails the build — because a boundary maintained by convention erodes within months under delivery pressure.

What it retains that microservices give up: transactions across the system when needed, refactoring a boundary as an ordinary code change rather than a migration, local debugging, one deployment, and no network between components.

What it gives up: independent deployment and scaling per module, and technology heterogeneity. For most systems, for a long time, that is a good trade.

The strategic argument that makes it more than a compromise: boundaries are hard to get right initially, and the modular monolith allows them to be moved cheaply while understanding improves. When a module genuinely needs independent scaling or ownership, a well-enforced boundary is the easiest thing in the world to extract — which is a far better position than discovering after distribution that the boundary was wrong.