Modular Monolith

Basic System Design
≈ 2 min read

One Process, but Explicit Boundaries Inside

A modular monolith keeps a single deployment but divides the application into large functional modules. Each module owns its area and exposes a small public interface to the rest of the system instead of allowing unrestricted access to every internal class.

Monolith
|-- Orders ---- public API
|-- Payments -- public API
`-- Users ----- public API

This structure reduces coupling before a network is introduced. A team can change a module implementation without forcing the rest of the application to know its internal tables, classes, and algorithm details.

The Boundary Must Be Enforced by Code

Simply putting classes into different directories is not enough. If the orders module directly uses internal payment repositories, the boundary exists only in a diagram. Useful modularity appears when dependencies pass through an explicitly defined contract.

At the same time, modules still run in one process, so communication remains fast and operations are much simpler than with a set of independent services.

Why This Is an Important Intermediate Option

A modular monolith lets you validate domain boundaries without paying the cost of a distributed system. If one module later truly needs to become a separate service, the existing interface and limited dependencies make that change much easier.

Boundaries first, processes second. If parts of a system cannot be isolated inside one application, moving them into separate services usually does not remove coupling— it only makes that coupling network-based.