From f7081e0a2cf56c88609aac5ff3b7fb50add50595 Mon Sep 17 00:00:00 2001 From: Joris Wouter Jonkers Date: Fri, 18 Sep 2026 12:44:31 +0200 Subject: [PATCH] docs(adr): ADR-001 says what is true about the domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three statements in it were false on main, and a reader who believed them would plan work that is already done. It said no domain exposes an `index.ts`. All eleven do, and each is named. It said the cross-domain rule had never had an implementation to route through, and that the rollout would be report-only first: both rules have been enforced as `error` since they landed. It described the rule as `no-restricted-paths`, which is not the plugin this repo has — both halves are one `no-restricted-imports` entry, and the reason they are one entry is now recorded, because a second block naming the same rule replaces the first. The allowlist is described as what it is: the debt count, 70 when the rule landed and 42 today, deleted when it empties. The `.vue` exception is written down as a decision rather than left to read as a gap. The naming sentence is the one thing not settled here. It asked for singular names, five domains are plural, and nothing enforces either; #1314 decides which way that goes. --- .../ADR-001-domain-feature-architecture.md | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/docs/adr/frontend/ADR-001-domain-feature-architecture.md b/docs/adr/frontend/ADR-001-domain-feature-architecture.md index aa3e60827..a8d80703d 100644 --- a/docs/adr/frontend/ADR-001-domain-feature-architecture.md +++ b/docs/adr/frontend/ADR-001-domain-feature-architecture.md @@ -38,10 +38,16 @@ never used once: what `board`, `cohort` and `esports` actually grew was code. `index.ts` is the exception — it was specified, it is the only way the -cross-domain rule below can be satisfied, and no domain has one. It is required. +cross-domain rule below can be satisfied, and every domain has one. It is +required: `association`, `auth`, `boards`, `cohorts`, `committees`, +`contribution`, `emails`, `esports`, `jobs`, `recovery` and `user` each name +what they offer through a door, and nothing outside a domain may reach past it. -**Domain names are singular and match the API module** — `board`, not `boards` — -so one word names the same capability on both sides of the wire. +**A domain is named for the API module it wraps.** Whether that name is singular +was specified here and never held to: `boards`, `cohorts`, `committees`, `emails` +and `jobs` are plural, the other six read as singular, and nothing enforces +either. #1314 settles which way it goes — until then, follow the directory that +is already there rather than this sentence. ### Dependency Rules - `app` may depend on `pages`, `domains`, `components`, `shared` @@ -57,20 +63,33 @@ so one word names the same capability on both sides of the wire. ### Enforcement -The rules above were convention-only, and the result is measurable: **63 of 109 -files under `pages` and `components` import the generated client directly** — 26 -of 47 pages and 37 of 62 components — against a prohibition written here. No -domain exposes an `index.ts`, so the cross-domain rule has never had an -implementation to route through. - -An eslint `no-restricted-paths` rule closes both, and lands **before** the -migration rather than after it, because an unenforced rule is what produced the -63: +Both rules are enforced. They are one `no-restricted-imports` entry in +`services/frontend/eslint.config.mjs` with two patterns, at `error`: - `pages/**` and `components/**` may not import `@/services/api`. -- `domains/a/**` may not import `domains/b/**` except `domains/b/index.ts`. - -Report-only first, then `error`. +- Nothing may import `@/domains//` except the door, `@/domains/`. + +One entry rather than two blocks, because a second block naming the same rule +replaces the first rather than adding to it — which is how the first draft of +this let a client import through. + +The rules were convention-only until then, and the cost was measurable: **63 of +109 files under `pages` and `components` imported the generated client +directly**. The files that predate the rule are named one by one in a +`CROSSES_THE_BOUNDARY` allowlist, which is the debt count — a new violation is a +red build, and the count cannot drift from what the build actually permits. It +stood at 70 when the rule landed and stands at **42** today: 7 pages and 35 +components. It is deleted when it empties. + +There is one exception, and it is a decision rather than a gap: **a `.vue` +component may be imported at its own path**. A component is imported where it is +drawn, and routing components through a barrel loads a domain's whole surface +into anything that renders one — which broke two unit suites and, measured, +saved nothing in the bundle. The regex carries that exception, and the rule +message says so. + +The second rule is scoped to `pages/**` and `components/**`. A domain deep-importing +another domain is not yet checked. ## Consequences