Chapter 11 — Modular Monolith Architecture Style
Why This Chapter Exists
Teams often treat “monolith” as synonymous with “bad” and “microservices” as synonymous with “modern.” In practice, many systems fail not because they are monoliths, but because they are unstructured monoliths.
This chapter describes the modular monolith: a single deployable unit organized by business domains rather than technical layers. It can provide meaningful modularity and team autonomy without the operational overhead of distributed systems—while still carrying specific risks if boundaries are not governed.
What Modular Monolith Is
A modular monolith is:
- monolithic in deployment (one deployable unit)
- domain-partitioned in structure (modules organized by business domain/subdomain)
Modules represent domain areas (and sometimes subdomains), grouping related functionality together.
Core Idea: Domain Partitioning Inside a Single Unit
Unlike layered architecture (technical partitioning), modular monolith emphasizes domain partitioning:
- “Orders,” “Billing,” “Accounts,” “Catalog,” etc.
- each module contains code relevant to that domain slice
This structure is simple, but it is also deceptively easy to erode.
A frequent failure mode is gradual collapse into an unstructured monolith (“big ball of mud”) due to:
- excessive code reuse across modules
- excessive module-to-module communication
Primary Risks
1) Boundary Erosion via Code Reuse
Code reuse is necessary in software development, but in a modular monolith excessive reuse can blur boundaries until modules become inseparable.
At that point the monolith becomes “unravelable”:
- heavily interdependent
- difficult to reason about
- expensive to change
2) Excessive Intermodule Communication
A modular monolith loses its benefits when modules constantly communicate. In a monolith, it is extremely convenient to instantiate classes across module boundaries.
That convenience creates an architectural gravity:
- module A directly references classes in module B
- compile-time dependency becomes the default path
When this happens at scale, it resembles “DLL hell” as module dependencies multiply and become fragile.
A high volume of intermodule communication can also indicate mis-modeled domains: if modules are not meaningfully independent, domain boundaries may have been poorly defined.
Communication Control: Mediator Approach
One strategy to reduce direct coupling is a mediator component:
- modules communicate through a mediator abstraction rather than referencing each other directly
This does not eliminate all coupling, but it can:
- simplify dependencies
- preserve module independence
- centralize integration logic
Data Topology in Modular Monolith
Because the system is deployed as a single unit, it often relies on a monolithic database topology (shared data store).
However, some modular monoliths keep domain-specific contextual data separated:
- modules may have their own schemas or bounded tables
- occasionally separate databases can exist even inside a monolith (less common, more complex)
The key point is not physical separation, but bounded ownership of data and behavior.
When the Monolith Is “Too Big”
A modular monolith’s main long-term risk is growth beyond maintainability.
Warning signals that it is getting too large:
- changes take too long to implement
- changes in one area unexpectedly break others
- teams block each other frequently
- startup time becomes excessive
These are not proof that the architecture is wrong, but they often correlate with:
- boundary drift
- rising coupling
- increasing cognitive load
Governance and Fitness Functions
Governance is often what determines whether modular monolith succeeds.
Two governance targets appear early:
- Define and enforce module boundaries
- ensure module structure stays meaningful
- prevent accidental cross-module imports
- Control intermodule communication
- minimize dependencies between modules
- make cross-module interaction deliberate and visible
This style is well-suited to automated structural checks because everything exists in one build artifact:
- import rules
- dependency graph checks
- cyclic dependency detection
Team Topologies Fit
Modular monolith often works best when teams are aligned to domain boundaries.
Common mappings:
- Stream-aligned teams
Teams can own domain flows end-to-end inside their module(s). - Enabling teams
Domain modules and clear boundaries make it easier for enabling teams to contribute tools, patterns, and guidance. - Complicated subsystem teams
Modules map naturally to complicated domain processing, reducing cognitive load for other teams.
Characteristic Profile (Qualitative)
Modular monolith typically scores well in:
- simplicity
- low cost
- maintainability (when boundaries hold)
- developer productivity
It scores poorly for high-demand operational characteristics such as:
- elasticity
- horizontal scalability
- fault tolerance
- high availability (as an inherent property)
Not because these are impossible, but because the architecture style does not optimize for them.
When It Fits (and When It Doesn’t)
Modular monolith tends to fit when:
- budgets and time constraints are tight
- the system is new and domains are still being clarified
- most changes are domain-oriented
- teams are practicing domain-driven design
It tends to be a poor fit when:
- the system requires high operational characteristics (scalability, elasticity, availability, fault tolerance)
- most changes are technical/infrastructure-driven rather than domain-driven
Frontend Context (React / Next.js Lens)
In frontend systems, a modular monolith maps naturally to:
- domain-based feature modules
- bounded ownership of routes, UI, state, and API adapters per domain
- a shared design system that remains infrastructure-like (not domain-owned)
- The same risks apply:
- over-sharing utilities until boundaries disappear
- allowing feature modules to import each other directly
- coupling state management across domains
- A modular monolith frontend can remain stable if module boundaries are treated as architectural constraints rather than folder conventions.
Closing Perspective
A modular monolith is not “microservices without microservices.” It is a deliberate architecture style with a clear trade: lower operational complexity in exchange for shared deployment and increased need for boundary governance.
If boundaries hold, it offers a pragmatic path to modularity at low cost. If boundaries erode, it becomes the architecture it was meant to avoid.
