intermediate 2 min answer

What mechanically enforces module boundaries in a monolith, and what happens without enforcement?

modular-monolithenforcementdependency-rulesownershipgitlabdesign
Show the full answer Hide the answer

What enforces boundaries

1. Static dependency checks in CI. Rules stating which modules may depend on which, failing the build on violation. This is the load-bearing control — everything else is convention, and convention loses to deadlines.

2. Explicit public interfaces per module. Everything else is internal and unreachable, enforced by the language's visibility mechanisms where they exist and by static analysis where they do not.

3. No shared database tables between modules. Each module owns its tables; others access through its interface. This is the boundary most often violated, because reaching into a table is easier than calling an interface — and once violated the schema becomes an unversioned shared contract.

4. Ownership per module, enforced by code ownership rules, so unowned code is a build failure.

5. Module-level tests that run without the rest of the application, which both proves independence and keeps the feedback loop fast.

What happens without enforcement

The boundaries erode, predictably and for rational reasons. A deadline justifies reaching across "just this once". A shared table is faster than an interface and both teams agree. A reorganisation leaves a module unowned and unowned code accretes. Nothing fails when structure degrades, because no test asserts it.

Within a couple of years the modular monolith is a big ball of mud, and the organisation concludes that modular monoliths do not work — when what did not work was the absence of a ratchet.

Why the enforcement matters more here than in microservices

In a microservices architecture the network enforces the boundary: you cannot reach into another service's memory. In a monolith nothing physically prevents a violation, so the enforcement must be explicit.

That is the entire trade: a monolith gives you cheap refactoring across boundaries and requires discipline to maintain them; services give you enforced boundaries and charge you distributed-systems costs for the enforcement.

The payoff worth naming

A well-enforced modular monolith produces exactly the evidence needed for a future decomposition: modules that have changed independently for a long period are proven extraction candidates, and modules that keep changing together are proof that the proposed boundary is wrong.

Choosing service boundaries from a diagram rather than from change history is why so many decompositions produce services that must be deployed together.