ADR-0030 — Import-direction rules: `lib ← components ← systems`
ADR-0030 — Import-direction rules: lib ← components ← systems #
- Status: Accepted
- Date: 2026-05-16
- Deciders: Natan
- Source:
docs/research/2026-05-09-master-synthesis.md↗
§2.4 — Convergence D: "new ADR pinning the full Dependency Rule (codifies what standards.ts enforces but never named)." PRD-13 §13.1.1 authors the ADR.
Context #
ADR-0001 pins the three-layer architecture (lib/, components/, systems/) but does not state the direction in which dependencies must flow between those layers. scripts/checks/standards.ts ↗ ruleDependencyRule (line 247) already enforces the direction mechanically, with rule-28-dependency IDs, but no ADR has named the rule. A reader of standards.ts sees the check; nothing explains why this is the rule.
Clean Architecture Ch. 22 (the Dependency Rule chapter) makes the inner-vs-outer-ring choice load-bearing: source-code dependencies must point only inward, from concrete frameworks toward abstract policy. SQA's three-layer architecture maps cleanly onto this: systems/ is outermost (system-specific orchestration), components/ sits in the middle (per-store capabilities), lib/ is innermost (pure infrastructure with no external coupling). config/ sits alongside lib/ as foundational input — the validated environment is consumed by every layer above it but consumes nothing.
Decision #
Source-code dependencies in src/ flow only inward:
systems/may import fromcomponents/andlib/.components/may import fromlib/only.lib/may import fromconfig/only — never fromcomponents/orsystems/.config/is foundational and may not import upward into any other layer (keeps the boot path straight: env validation runs before anything else).
This is the rule already enforced by scripts/checks/standards.ts:247 ↗ (rule-28-dependency, severity fail). This ADR is the name for that mechanical check, not new behaviour.
Asymmetry note. config/ is innermost-equivalent to lib/ — both are foundational and sit alongside each other in the concentric model. lib/ may import config/ (the validated env is foundational input that lib/ modules read from). config/ may not import lib/ to keep the boot path straight (env validation happens before any lib/ module loads).
src/index.ts (the runner entry point) and src/runners/* sit outside the four layers — they compose systems/ and may import from any inner layer. They do not introduce a fifth layer; they are the main in the Clean Architecture sense (Ch. 26).
Consequences #
Positive.
- The Dependency Rule survives gate-script rewrites — a reader of
the rule can find the rationale here, and a refactor of standards.ts can't silently change the direction.
- New ADRs that introduce new layers (e.g.
src/domains/) have a
pin to reason against — they must extend this ADR or supersede it, not silently widen the allowed graph.
- The
config/asymmetry is recorded once, here, instead of being
re-derived per-reviewer.
Negative.
- One more ADR for a reader to know about. Mitigated by
See also
cross-links from standards.ts (// see ADR-0030) and from code-standards.md ↗ Rule 28.
Falsifiability #
Revisit this ADR if any of the following becomes true:
- A new layer is introduced (e.g.
domains/,policies/).
The direction must extend to include it, and the asymmetry table in §Decision needs a new row.
- A refactor forces a genuine cycle that DIP can't resolve.
Clean Architecture Ch. 14 prescribes two cycle-breaking paths: (a) DIP via interface declared in the inner layer; (b) extract a Common Component both sides depend on. If neither works for a real refactor, the layer model itself needs revision — supersede this ADR.
config/ever needs to import fromlib/. The boot path
would no longer be straight; either config/ absorbs the needed primitive, or the layer model changes.
See also #
- ADR-0001 — the three-layer
architecture this ADR adds direction to.
- ADR-0009 — the
discipline-name constraint that bounds when a new layer is justified.
- ADR-0010 — the path aliases
(@lib/, @components/, @systems/, @config/) that make the direction grep-able.
- ADR-0040 — the
runners/ folder as the fifth top-level slot; runners sit alongside src/index.ts and may import from any inner layer.
Rule 28 — the standards-rule entry for this ADR.
ruleDependencyRule (line 247) — the mechanical enforcement.
§2.4 (Convergence D) — the research convergence that motivated the ADR.
- PRD-13 ↗ §13.1.1 — the PRD that
authored this ADR.