Provides a measurable decision record for choosing a modular monolith when one deployment can meet scale and availability needs while strict internal boundaries preserve future extraction options.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Choose a modular monolith when the team benefits from one transactional and deployment boundary, yet domains can be assigned to explicit modules with enforced dependency direction. Revisit the choice when independent scaling, fault isolation, or release ownership becomes a demonstrated need.
Scope
- Adoption criteria, boundary rules, dependency enforcement, ownership, and exit triggers.
- A single deployable with module-private code and data access surfaces.
- An ADR that records evidence, rejected alternatives, and a scheduled review.
Outside this block
- Claiming that a folder structure alone creates modularity.
- Designing the final microservice topology before extraction is justified.
Contract
- Each business capability has one owning module and a documented public interface.
- Cross-module calls use public interfaces; direct imports of internals and direct writes to another module's tables fail CI.
- Transactions may span modules only through an explicitly listed application use case.
- Module dependency cycles are prohibited or recorded as time-bounded exceptions.
- Exit triggers use observed data such as release contention, scaling cost, blast radius, or ownership conflict.
Implementation guidance
- Start with compile-time or lint dependency rules and architecture tests.
- Use schemas or table prefixes plus database roles where feasible to reveal unauthorized cross-module access.
- Publish domain events after commit for asynchronous consumers; do not use an in-process event bus to hide circular calls.
- Record an extraction seam: interface, owned data, callers, and consistency expectations.
Failure handling and safeguards
- A boundary exception needs an owner, reason, and removal date; otherwise CI rejects it.
- If module extraction would break an invariant, document the invariant rather than pretending the seam exists.
- When deployment coupling becomes the limiting constraint, open a new ADR instead of gradually creating an unowned distributed system.
Verification and operations
- Measure cross-module dependency count, cycles, exception age, change coupling, and deployment rollback rate.
- Review quarterly and whenever an exit trigger remains above threshold for two review periods.
- Run architecture tests in pull requests and visualize dependency changes.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.
References
- No external normative source is required; this package defines a project decision.