Modular Monolith
A modular monolith keeps one deployable application while enforcing strong internal module boundaries.
Core Idea
A modular monolith is a single deployable unit that is internally decomposed into well-defined modules. Each module owns its data, exposes a public API to other modules, and hides its implementation details. Unlike a traditional monolith where everything is tangled together, a modular monolith maintains clear boundaries while avoiding the operational complexity of distributed systems.
Use Cases
- Teams that need better structure but are not ready for distributed systems
- Applications that are growing beyond a simple layered architecture
- Organizations that want a path toward microservices without committing upfront
- Domains with complex business logic that benefits from module isolation
- Systems where deployment simplicity is valued over independent scaling
Module Boundaries
Each module should:
- Have a clearly defined public interface (API surface)
- Hide its internal implementation from other modules
- Own its data — no shared database tables between modules
- Communicate with other modules only through their public interfaces
- Be aligned with a bounded context or business capability
Data Ownership
In a well-designed modular monolith, each module owns its database tables. Other modules cannot query another module's tables directly — they must go through the module's public API. This ensures that internal schema changes do not ripple across the system.
In practice, this is often enforced through separate database schemas, namespace conventions, or architectural fitness functions that detect cross-module data access.
Benefits
- Deployment simplicity: One artifact to build, test, and deploy
- No network boundaries: Module communication is in-process, avoiding serialization and network failures
- Transactional consistency: Modules can participate in the same database transaction when needed
- Easier refactoring: Module boundaries can be adjusted without changing deployment topology
- Lower operational cost: No service mesh, no distributed tracing required, no inter-service authentication
- Path to microservices: Well-defined modules can be extracted into services later if needed
Trade-offs
- Discipline required: Without enforcement, module boundaries erode over time
- Single deployment unit: All modules must be deployed together, even for small changes
- Scaling limitations: Cannot scale individual modules independently
- Technology lock-in: All modules share the same runtime, language, and framework
- Team coordination: Teams working on different modules may still create merge conflicts
How It Differs from a Distributed Monolith
A distributed monolith is a system that has the deployment complexity of microservices but the coupling of a monolith. Services cannot be deployed independently because they depend on each other's internals, share databases, or require coordinated releases.
A modular monolith avoids this trap by being honest about its deployment model: it is one application, and it embraces that simplicity. The focus is on internal modularity, not on distribution.
Comparison with Microservices
| Aspect | Modular Monolith | Microservices |
|---|---|---|
| Deployment | Single unit | Independent per service |
| Communication | In-process method calls | Network (HTTP, messaging) |
| Data isolation | Separate schemas, same database | Separate databases per service |
| Consistency | Strong (transactions available) | Eventual (sagas, compensation) |
| Operational complexity | Low | High |
| Team independence | Moderate | High |
| Scaling | Whole application | Per service |