Lesson 7 / 25
Modules With Real Boundaries
Structure a monolith into modules with public APIs and enforced dependencies.
Microservice-style boundaries, monolith-style deployment
A modular monolith divides the code into modules aligned with business capabilities (catalogue, ordering, billing), each with a small public API and hidden internals. Other modules may call only that API, never internal classes. Dependencies form a directed acyclic graph: ordering may depend on catalogue, but not the reverse. Enforce the rules automatically, because conventions erode: Java teams use ArchUnit tests, the Java module system, or Spring Modulith; .NET teams use separate projects with internal visibility and architecture tests; TypeScript monorepos use tools such as Nx module boundaries or dependency-cruiser. Modules communicate through direct calls on their API, or through in-process events for loose coupling. The result keeps the simplicity of one deployment while preparing clean seams if a module ever needs to become a service.
Modules with public APIs
Each module exposes a small API; internal parts are hidden from other modules.
Enforcing module boundaries with ArchUnit (Java)
The build fails if billing code reaches into ordering internals.
@AnalyzeClasses(packages = "com.shop")
class ModuleBoundaryTest {
@ArchTest
static final ArchRule only_api_packages_are_used_across_modules =
classes().that().resideInAPackage("com.shop.ordering.internal..")
.should().onlyBeAccessed().byAnyPackage("com.shop.ordering..");
@ArchTest
static final ArchRule catalogue_does_not_depend_on_ordering =
noClasses().that().resideInAPackage("com.shop.catalogue..")
.should().dependOnClassesThat().resideInAPackage("com.shop.ordering..");
}Make illegal dependencies fail the build
A boundary that is only documented will be crossed under deadline pressure. A failing test in CI is much harder to ignore.
Quick check: What makes a modular monolith different from a big ball of mud?
- Modules have explicit public APIs and enforced dependency rules
- It is deployed as several services
- It uses a NoSQL database
- It has no database
Answer
Modules have explicit public APIs and enforced dependency rules — Enforced module boundaries are the defining property; deployment is still a single unit.