# WP-03 — Boundaries I: contracts purity + ApiClient confinement Status: todo Phase: 0 — enforcement & gates ## Why `contracts/` is the FE⇄BE wire seam and must import **nothing** (CLAUDE.md §1, ADR-0001) — but `registratie/contracts/dashboard-view.dto.ts` imports domain types, and `change-request-form.component.ts` injects `ApiClient` straight into UI. Neither rule is lint-enforced. Rule + fixes land together so this WP ends green. ## Read first - `CLAUDE.md` §1 + §4 (layers, BFF-lite) - `eslint.config.mjs` (existing zone style to extend) - `src/app/registratie/contracts/dashboard-view.dto.ts` - `src/app/registratie/infrastructure/dashboard-view.adapter.ts` - `src/app/registratie/ui/change-request-form/change-request-form.component.ts` (~line 53) - `src/app/registratie/application/submit-change-request.ts` - `src/app/shared/infrastructure/role.ts` and `src/app/brief/ui/letter-composer/letter-composer.component.ts` (line 2) ## Decisions (pre-made, don't relitigate) - Wire DTOs express enums as plain string-literal unions **inlined in the DTO file** — they describe the wire, not the domain. The adapter's `parse*` maps them to domain types. - The `Role` **type** moves out of infrastructure (to `shared/domain` or `shared/application` — pick where its current consumers point most naturally); the role-reading mechanism (`role.interceptor.ts` etc.) stays infrastructure. ## Files - `eslint.config.mjs` — two new restrictions - `src/app/registratie/contracts/dashboard-view.dto.ts` — remove domain imports - `src/app/registratie/infrastructure/dashboard-view.adapter.ts` — absorb the mapping - `src/app/registratie/ui/change-request-form/change-request-form.component.ts` — stop injecting `ApiClient`; the component calls the application command, which owns the client dependency (move the `inject` into `submit-change-request.ts` or a thin registratie adapter, matching how other commands get the client) - `src/app/shared/infrastructure/role.ts` + new home for the `Role` type + the `letter-composer.component.ts` / `brief.store.ts` import sites ## Steps 1. Rewrite `dashboard-view.dto.ts` as pure wire shapes (no imports at all); update the adapter's `parse*`/`toDomain` to map wire → domain; run the adapter spec. 2. Move the `Role` type; update import sites (mechanical; lint will find them). 3. Refactor change-request-form: UI keeps dispatching to the machine; the submit command receives/owns the `ApiClient` (pattern: other `submit-*.ts` commands). 4. Add eslint zones: - `src/app/**/contracts/**` may import nothing from `@angular/*`, any `@*` context alias, or relative app code. - `@shared/infrastructure/api-client` importable only from `**/infrastructure/**` (+ the app config/provider and the generated file itself). 5. Lint the repo; there must be zero new disables. ## Acceptance criteria - [ ] `dashboard-view.dto.ts` has no import statements. - [ ] `grep -rn "api-client" src/app --include="*.ts" | grep -v infrastructure` returns only the generated client itself (and its provider wiring, if outside infra). - [ ] Both eslint rules active; a planted violation fails lint. - [ ] Change-request flow works (existing machine/command specs pass). ## Verification GREEN + `npm run test-storybook:ci`. Manual smoke: `npm start` → Gegevens wijzigen → submit a change request. ## Out of scope The `ui ↛ infrastructure` rule for adapter injections in wizards — that's WP-04. ## Risks Role-type move ripples through brief imports — mechanical; run tests before/after.