Enforcing layer boundaries in CI
Architecture decisions kept in documents decay in six months. Wiring the boundary into the build turns the document into code.
· 4 min
"Business rules will not attach to HTTP" is a sentence everyone approves in a meeting. Three months later there is an `if (invoice.status === ...)` inside an endpoint.
The problem is not intent but feedback: nothing happens when you break the rule. It is caught in review, if it is caught.
Turning the rule into a package boundary
The repo is split into four apps (api, web, field, admin) and five packages. `core` holds only business rules; importing HTTP, ORM or a framework is forbidden. `db` holds data access, `shared` holds types, `ui` holds components, `config` holds configuration.
Five rules, checked at build
- core cannot import db, HTTP or a framework.
- api calls core and cannot restate a business rule.
- ui cannot depend on api or db.
- shared cannot depend on any package.
- Apps cannot import one another; they talk through packages.
These five rules live in a dependency-cruiser config and run on every build. A breach means a red build. The rule is no longer an opinion; it is structure.
What it does not fix
A layer boundary does not fix a bad function inside `core`. It blocks cross-package dependency, not mess inside a package. Tests and review are still required.
There is a cost too: when a shared helper is needed, placing it in the right package takes thought. Sometimes "where does this belong" takes five minutes. In return, six months later nobody asks "why is this called from here".
Maintenance Management (CMMS)
Multi-company maintenance flow: fault → SLA → work order → field → closure.