Skip to content
SQA Cockpit

ADR-0030 — Import-direction rules: `lib ← components ← systems`

ADRsUpdated 3 min readEdit on GitHub ↗

ADR-0030 — Import-direction rules: lib ← components ← systems #

§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 from components/ and lib/. components/ may import from lib/ only. lib/ may import from config/ only — never from components/ or systems/. 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:

  1. 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.

  1. 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.

  1. config/ ever needs to import from lib/. The boot path

would no longer be straight; either config/ absorbs the needed primitive, or the layer model changes.

See also #

architecture this ADR adds direction to.

discipline-name constraint that bounds when a new layer is justified.

(@lib/, @components/, @systems/, @config/) that make the direction grep-able.

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.

authored this ADR.

Was this page helpful?