The Problem
Modules need to react to changes in other modules without creating tight coupling. Direct method calls create dependencies that make the system rigid.
The Solution
Use integration events to communicate between modules. Modules publish events when something significant happens, and other modules can subscribe to react.
Benefits
- Loose coupling - Modules don’t need to know about each other
- Flexibility - Easy to add new subscribers
- Auditability - Events provide a clear record of what happened
- Path to microservices - Events translate well to message queues
Learn more →The Problem
In a monolithic application, it’s easy for code to become tangled. Without clear boundaries, dependencies grow organically and the system becomes difficult to change.
Module boundaries help prevent this by establishing clear contracts between parts of the system.
The Solution
Define modules based on business capabilities, not technical layers. Each module should:
- Own its data
- Expose a clear API
- Hide its implementation details
- Communicate with other modules through well-defined contracts
Implementation
There are several ways to enforce module boundaries in your codebase:
Learn more →The Problem
Modules need to share some common code—value objects, interfaces, and utilities. But sharing too much creates hidden coupling.
The Solution
Create a small, carefully curated shared kernel that contains only the most fundamental, stable abstractions.
Guidelines
Keep It Small
The shared kernel should be minimal. If it grows large, it becomes a source of coupling.
Keep It Stable
Changes to the shared kernel affect all modules. Treat changes with care.
Learn more →