An architectural boundary that exists only in a diagram is a boundary that has already been crossed, and the enforcement is a morning’s work.
# deptrac.yaml
layers:
- name: Billing
collectors: [{ type: className, regex: ^App\Billing\.* }]
- name: Ordering
collectors: [{ type: className, regex: ^App\Ordering\.* }]
- name: Shared
collectors: [{ type: className, regex: ^App\Shared\.* }]
ruleset:
Billing: [Shared]
Ordering: [Shared]
Shared: ~
A violation becomes a failed build rather than a review comment somebody may or may not make, which is the entire difference. Starting with a baseline of existing violations and forbidding new ones is the tractable path on a codebase that has never had this — the same ratchet as a static analysis baseline. A grep-based check in a shell script covers most of it if adding a dependency is unwelcome.