A module's root package is its default API boundary; another module should not import classes from its internal packages.
Spring Modulith module APIs: call the inventory root package, not its internals
Arrange by capability
The isolated source project places InventoryQueries in the inventory root package and StockStore in inventory.internal. DispatchPlanner imports InventoryQueries. ApplicationModules.verify accepts that graph. The main application's direct child packages become the two modules in this simple arrangement. Constructor injection keeps the dependency visible.
Make the forbidden import fail
A second fixture imports a public class from inventory.internal. Java compiles it, but Modulith verification rejects it. That test catches coupling that would make an internal refactor break dispatch. A module API is not a network boundary or a permission check. The same process still needs transaction and tenant rules for data mutations.
Checked excerpt
ApplicationModules.of(ReceiptModulesApplication.class).verify();Cost and verification
Verification scans compiled classes during tests. A valid module call remains a local method call and adds no network hop. The isolated source-kit project checks the stated local behavior.
Common Mistakes
- Do not expose an internal type merely to satisfy another module's import.
- Do not equate a package rule with tenant authorization.
- Do not make every class part of the module API.
Read next
Spring Modulith verification: a compiling internal import still fails architecture checks, Spring Modulith module tests: distinguish structure verification from a sliced context, Spring constructor injection: required dependencies stay visible, Spring method authorization: reject a cross-tenant read.
Related Spring path
Continue with Spring Modulith module slices: start inventory without dispatch.
