How do you enforce boundaries in a modular monolith?
“A modular monolith is only modular if the boundaries hold. What mechanisms actually keep the Order module from reaching into the Payment module's tables, and how does that prepare a later extraction?”
What this tests
- Concrete enforcement mechanisms, not just intent
- Understanding of data ownership inside one database
- How in-process events replace direct calls
- The path from module to service and what changes at extraction
Answers by level
Read the beginner answer first and notice what is missing.
Three mechanisms. First, a public API per module: a small set of exported functions or an interface; everything else is private, and a build rule (package visibility, an import linter, an architecture test) fails the build on an import from another module's internals. Second, data ownership: each module owns its tables, and no other module queries them — the Order module gets payment status through payments.getStatus(orderId), not through a join. Third, in-process events for consequences: when an order is placed, the Order module publishes OrderPlaced in the same transaction and the Notification module subscribes, so there is no direct call and no reverse dependency.
This sets up extraction. If the Payment module already has a public API and owns its tables, moving it behind HTTP means replacing the in-process implementation of that interface with a client, and moving its tables to their own database. The in-process events become messages on a queue. What changes is the failure model — the call can now time out — so the extraction is when timeouts, retries and idempotency get added, not before.
Green flags · Red flags
- Names build-time enforcement (import rules, package visibility, architecture tests)
- Requires per-module table ownership with no cross-module queries
- Uses in-process events for consequences to avoid reverse dependencies
- Explains that extraction swaps an implementation for a client and moves tables
- Notes that cross-module transactions are the hidden coupling
- "Folders per domain and code review are enough."
- Allows cross-module joins because they are convenient
- Thinks extraction is moving a folder to a new repo
- Does not mention that the failure model changes when the boundary becomes a network