Spring Modulith Modular Application Guide
Spring Modulith modular architecture enables you to build well-structured monolithic applications with clear module boundaries enforced at compile time. Therefore, you get the simplicity of a monolith with the modularity benefits typically associated with microservices. This guide covers practical implementation patterns for production applications.
The Case for Modular Monoliths
Microservices introduce distributed system complexity including network failures, data consistency challenges, and operational overhead. Moreover, most teams adopt microservices prematurely before understanding their domain boundaries. As a result, they end up with a distributed monolith that combines the worst of both approaches.
In contrast, a modular monolith runs as a single deployment unit while maintaining strict internal boundaries. Consequently, refactoring is simpler and you can extract modules into services later when the boundaries are well understood.
Modular application structure with clear package boundaries
Defining Module Boundaries with Spring Modulith
Spring Modulith uses package conventions to define modules. Additionally, each top-level package under your application base package becomes a module with its own public API:
// Module structure
com.example.app/
├── order/ // Order module
│ ├── OrderService.java // Public API
│ ├── Order.java
│ └── internal/ // Hidden from other modules
│ ├── OrderProcessor.java
│ └── OrderValidator.java
├── inventory/ // Inventory module
│ ├── InventoryService.java
│ └── internal/
│ └── StockCalculator.java
└── shipping/ // Shipping module
├── ShippingService.java
└── internal/
└── CarrierClient.java
Classes in the internal package are invisible to other modules. Therefore, Spring Modulith enforces encapsulation that plain Java packages cannot guarantee.
Event-Based Module Communication
Modules communicate through Spring application events rather than direct method calls. Furthermore, this decoupling enables modules to evolve independently. However, synchronous events still execute within the same transaction for consistency.
For example, when an order is placed, the order module publishes an OrderPlaced event. Meanwhile, inventory and shipping modules listen and react without direct dependencies on the order module.
Event-driven communication between application modules
Spring Modulith Modular Testing
Module tests verify that each module works correctly in isolation. Specifically, the @ApplicationModuleTest annotation bootstraps only the module under test with its dependencies. As a result, tests are faster and failures point directly to the affected module.
Additionally, architecture tests validate module dependency rules at build time. Consequently, accidental coupling between modules fails the build before it reaches production.
Documentation and Observability
Spring Modulith generates module documentation including dependency diagrams and event catalogs automatically. Moreover, it integrates with Actuator to expose module health endpoints. Therefore, operations teams can monitor module boundaries in production and detect architecture drift.
Auto-generated module documentation and dependency visualization
Related Reading:
Further Resources:
In conclusion, Spring Modulith modular architecture bridges the gap between monoliths and microservices by enforcing clean boundaries within a single deployment. Therefore, start with a modular monolith and extract services only when domain boundaries are proven.