diff --git a/CLAUDE.md b/CLAUDE.md index 916db0a1..da19522b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -268,6 +268,24 @@ you are using available components rather than building custom ones. - Never: TypeScript `any` type (use proper types, generics, or `unknown` with type guards) - Never: import axios directly in a component (use `src/lib/axios.ts` via a composable) +## Frontend import boundaries (apps/app/) + +`apps/app/` enforces a layered import architecture via +`eslint-plugin-boundaries`. Ten zones (`types` → `utils` → `lib` → +`plugins` / `composables` / `stores` / `navigation` → `components` → +`layouts` → `pages`); each zone may only import from the zones below +it in the matrix. Vendored `@core/` and `@layouts/` are exempt. +Cross-zone violations are lint errors, not warnings. + +Matrix details + rationale: `dev-docs/WS-3-SESSION-1C-AUDIT.md`. +Config: `apps/app/.eslintrc.cjs`. + +When adding a new file: pick the zone first. If your file imports +from a zone the matrix forbids, the structural answer is usually to +hoist a type to `types/` or extract a helper to `utils/` / +`composables/` — not to disable the rule. Per-line disables are +allowed only with a `TODO TECH-*` reference to a backlog item. + ## Order of work for each new module 1. Create and run migration(s) diff --git a/dev-docs/ARCH-CONSOLIDATION-2026-04.md b/dev-docs/ARCH-CONSOLIDATION-2026-04.md index 8e48d2e3..af920cde 100644 --- a/dev-docs/ARCH-CONSOLIDATION-2026-04.md +++ b/dev-docs/ARCH-CONSOLIDATION-2026-04.md @@ -551,6 +551,30 @@ Zie §4 voor scope en stappen. drie geplande wijzigingen, doorschuiven naar follow-up. WS-3 lint cleanup workstream effectief afgerond; sessie 1c (eslint-plugin-boundaries) kan starten op een schone baseline. +- **Sessie 1c (2026-04-30)** — _import-boundaries enforcement, klaar._ + `eslint-plugin-boundaries@6.0.2` (MIT, ESLint ≥6 peer-dep, Node ≥18.18) + toegevoegd als directe devDep aan `apps/app/package.json` per de + TECH-PORTAL-ESLINT-DEPS lesson, en geactiveerd in `apps/app/.eslintrc.cjs` + met layered-architecture matrix: 10 zones (`types`, `utils`, `lib`, + `plugins`, `composables`, `stores`, `navigation`, `components`, `layouts`, + `pages`) met richtgevende edges (rationale + bewijs in + `dev-docs/WS-3-SESSION-1C-AUDIT.md`). Vendored `@core/`, `@layouts/`, + het dode `views/` bestand en orchestratie-roots `App.vue` + `main.ts` + staan in `boundaries/ignore`. Vier `lib → stores` violations in + `lib/axios.ts` (regels 3, 4, 61, 72) gemarkeerd met per-line + `eslint-disable-next-line` comments referencerend naar + `TECH-AXIOS-STORE-COUPLING` — de structurele decoupling van axios is + bewust uitgesteld naar een dedicated sessie omdat het architectuurwerk + is, geen tooling-cleanup. Boundary-rule blijft `error`, matrix blijft + strict; toekomstige `lib/X.ts` schrijvers stoten alsnog tegen de regel. + Lint baseline 0 errors / 0 warnings; build smoke groen; Vitest groen + (49 tests). Drie backlog-items aangemaakt voor toekomstige actie: + `TECH-AXIOS-STORE-COUPLING` (decouple axios), `TECH-DELETE-DEAD-VIEWS` + (verwijder `src/views/`), `TECH-WS3-BOUNDARIES-SUBZONES` (sub-zone + enforcement na PR-B), `TECH-WS3-BOUNDARIES-ROUTER-ZONE` (matrix-update + wanneer `plugins/1.router/` naar `router/` verhuist). WS-3 lint cleanup + + boundaries enforcement effectief afgerond; volgende WS-3 stap is PR-B + (portal merge) zodra WS-6 sessie 2 in main is geland. **Klaar-criteria:** - `apps/portal/` is verwijderd diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index 815a0ce7..bfce4e71 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -622,6 +622,139 @@ TECH-ESLINT-V9-MIGRATION zijn natuurlijke kandidaten). --- +### TECH-AXIOS-STORE-COUPLING — Decouple lib/axios.ts from stores layer + +**Aanleiding:** WS-3 sessie 1c (eslint-plugin-boundaries enforcement) +constateerde dat `apps/app/src/lib/axios.ts` 4 imports heeft uit `stores/` +(2 statisch op regel 3-4 voor `useNotificationStore` / +`useOrganisationStore`, 2 dynamisch op regel 61, 72 voor +`useImpersonationStore` / `useAuthStore` uit 1b-iii). De `lib → stores` +edge schendt de layered-architecture matrix. Om sessie 1c on-time te +landen zijn de 4 sites gemarkeerd met `eslint-disable-next-line` + +verwijzing naar dit backlog-item; de structurele fix is bewust uitgesteld +naar een dedicated sessie omdat het architectuurwerk is, geen tooling- +cleanup. + +**Wat:** +- Decouple `lib/axios.ts` van stores zodat het puur HTTP-infrastructuur + wordt. Twee paden, kies bij refactor: + - **Approach 1 (preferred starting point):** `lib/axios.ts` exporteert + de axios-instance plus een `registerInterceptors(client, deps)` + functie die callbacks accepteert (`onAuthFail`, + `onImpersonationDrop`, `getActiveOrgId`, `notify(message, level)`). + Een nieuwe `plugins/axios-bindings.ts` (mag `stores` importeren per + matrix) roept `registerInterceptors` aan bij app-init met closures + over de stores. + - **Approach 2 (fallback):** event-bus / callback registry; axios.ts + emit-eert semantische events (`auth-failed`, `needs-org-header`, + `notify-error`) en `plugins/axios-bindings.ts` subscribet. +- Verwijder alle 4 `eslint-disable-next-line` comments uit + `lib/axios.ts`. +- Tests moeten dekken: X-Organisation-Id header injection, 401/403 + logout flow, impersonation revocation flow, error toast op 4xx/5xx. +- Optioneel: meteen ook de static/dynamic import-split in axios.ts + uniformeren (nu inconsistent om legacy 1b-iii-redenen). + +**Prioriteit:** Middel — geen blokker voor andere workstreams, maar elke +maand dat dit blijft staan is een vlek op de boundaries-enforcement +geloofwaardigheid. Aanbevolen: meelift met de eerste WS-3 PR die `lib/` +of `plugins/` raakt, of een dedicated 2-3 uur sessie na WS-6 sluiting. + +--- + +### TECH-DELETE-DEAD-VIEWS — Verwijder src/views/ uit apps/app/ + +**Aanleiding:** WS-3 sessie 1c audit (`dev-docs/WS-3-SESSION-1C-AUDIT.md` +§A.1) constateerde dat `apps/app/src/views/` precies één bestand bevat +(`views/pages/authentication/AuthProvider.vue`) met nul importers in de +hele repo. Het is overgebleven Vuexy-template dode code. De §4.2 +post-consolidatie target layout drop `views/` volledig. Het bestand is +nu in `boundaries/ignore` opgenomen om sessie 1c te laten landen, maar +de natuurlijke vervolgstap is fysieke verwijdering. + +**Wat:** +- Verwijder `apps/app/src/views/` recursief. +- Verwijder de bijbehorende `'src/views/**'` regel uit + `apps/app/.eslintrc.cjs` `boundaries/ignore`. +- Verifieer dat `pnpm lint`, `pnpm build` en `pnpm test` groen blijven. +- Eén commit: `chore(cleanup): delete dead Vuexy views/ directory`. + +**Prioriteit:** Laag — triviaal cleanup-item, kan in elke gerelateerde +housekeeping-sprint meeliften (bijvoorbeeld vóór of na WS-3 PR-B). + +--- + +### TECH-WS3-BOUNDARIES-SUBZONES — Sub-zone import-boundaries inside components/ and pages/ + +**Aanleiding:** WS-3 sessie 1c heeft top-level zone-boundaries in +`apps/app/` neergezet via `eslint-plugin-boundaries`. De `/dev-docs/ARCH-CONSOLIDATION-2026-04.md` +§4.2 target layout introduceert sub-zones binnen die top-level zones — +specifiek `components/{organizer,portal,shared}/` en +`pages/{(auth),portal,register,events,persons,organisations,platform}/`. +De architecturale intent is dat `components/portal` niet uit +`components/organizer` mag importeren (en vice versa), met `shared` als +de gemeenschappelijke uitgang. Sessie 1c heeft die sub-zone +enforcement bewust uitgesteld omdat de sub-folders nog niet bestaan; +pre-emptieve rules op niet-bestaande directories worden stille dode +config die drift. + +**Wat:** +- **Precondition:** WS-3 PR-B is gemerged en de §4.2 sub-folder + structuur is gelandt (`components/{organizer,portal,shared}/` en + `pages/{(auth),portal,...}/` bestaan fysiek met content). +- Breid `boundaries/elements` in `apps/app/.eslintrc.cjs` uit met: + - `{ type: 'components-organizer', pattern: 'src/components/organizer/**' }` + - `{ type: 'components-portal', pattern: 'src/components/portal/**' }` + - `{ type: 'components-shared', pattern: 'src/components/shared/**' }` + - (ontworpen sub-zones voor `pages/` analoog) +- Voeg per-sub-zone rules toe: `components-portal` en + `components-organizer` mogen beide uit `components-shared` importeren, + maar niet uit elkaar. `pages/portal/` mag niet uit `pages/events/` + (en de andere organizer-pages) importeren, en omgekeerd. +- Resolve violations die bij eerste activatie naar boven komen. +- ETA: 1-2 uur zodra precondities ervoor liggen. + +**Prioriteit:** Middel — preventieve architectuur-discipline voor de +multi-tenant context-isolatie tussen organizer en portal UI-paden. +Zonder deze rules is de kans groot dat een ontwikkelaar tijdens een +PR-B follow-up onbewust portal- en organizer-componenten verstrengelt. + +--- + +### TECH-WS3-BOUNDARIES-ROUTER-ZONE — Add `router/` zone to boundaries matrix + +**Aanleiding:** WS-3 sessie 1c audit (§3 forward-compatibility) flagde +dat de §4.2 target layout `src/plugins/1.router/` vervangt door een +flat `src/router/`. De huidige boundaries-matrix in +`apps/app/.eslintrc.cjs` mapt router-files naar de `plugins` zone +(omdat ze fysiek in `src/plugins/1.router/` zitten). Zodra de +verhuizing plaatsvindt — geplant in een latere WS-3 PR — moet de +matrix-config dat reflecteren, anders vallen router-files buiten de +`boundaries/elements` mapping en flag-stormt de plugin met "no rule +found". + +**Wat:** In dezelfde commit/PR die `src/plugins/1.router/` naar +`src/router/` verhuist: + +- Voeg toe aan `boundaries/elements` in `apps/app/.eslintrc.cjs`: +```js + { type: 'router', pattern: 'src/router/**' }, +``` + Plaats vóór `plugins` in de array (first-match-wins ordering). +- Voeg toe aan `boundaries/element-types` rules: +```js + { from: 'router', allow: ['types', 'utils', 'lib', 'plugins', 'stores'] }, +``` +- Verifieer `pnpm lint` blijft op 0 problemen. + +**Trigger:** "src/plugins/1.router/" → "src/router/" verhuizing (latere +WS-3 PR, vermoedelijk PR-B als die de router-tree consolideert). + +**Prioriteit:** Laag — geen actie tot de verhuizing plaatsvindt; dan +verplicht 5-minute follow-up. + +--- + ### TECH-08 — Paginated response meta wordt weggegooid in organizer composables **Aanleiding:** `apps/app/src/composables/api/useSections.ts` en