Lint-enforce two architecture rules that were only documented (ADR-0001), landing the rules with the fixes so the build stays green: - contracts/ imports nothing: dashboard-view.dto.ts is now pure wire shapes (inline string-union enums, no domain imports). The DashboardView FE-view type moves to the adapter, which maps wire → domain (compiler-enforced seam). - ApiClient lives only in infrastructure: change-request-form (UI) no longer injects ApiClient — a new ChangeRequestAdapter owns the client and the submit becomes a createSubmitChangeRequest() command factory (createDraftSync shape). draft-sync's wire-DTO import becomes type-only (allowed via allowTypeImports). - Role type moves to shared/domain/role.ts; the ?role= reader stays in shared/infrastructure/role.ts. - eslint: contracts import-ban + @typescript-eslint/no-restricted-imports on api-client (value-only; type imports permitted; infra + shared/upload exempt). Also fixes a PRE-EXISTING bug found while verifying the flow: change-request-form never imported FormsModule, so (ngSubmit) didn't bind and the submit button did a native form submit (page reload) instead of submitting. Verified end-to-end in the running app: submit → command → adapter → backend → reference, success alert shown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.8 KiB
3.8 KiB
WP-03 — Boundaries I: contracts purity + ApiClient confinement
Status: done (pending commit) 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.tssrc/app/registratie/infrastructure/dashboard-view.adapter.tssrc/app/registratie/ui/change-request-form/change-request-form.component.ts(~line 53)src/app/registratie/application/submit-change-request.tssrc/app/shared/infrastructure/role.tsandsrc/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
Roletype moves out of infrastructure (toshared/domainorshared/application— pick where its current consumers point most naturally); the role-reading mechanism (role.interceptor.tsetc.) stays infrastructure.
Files
eslint.config.mjs— two new restrictionssrc/app/registratie/contracts/dashboard-view.dto.ts— remove domain importssrc/app/registratie/infrastructure/dashboard-view.adapter.ts— absorb the mappingsrc/app/registratie/ui/change-request-form/change-request-form.component.ts— stop injectingApiClient; the component calls the application command, which owns the client dependency (move theinjectintosubmit-change-request.tsor a thin registratie adapter, matching how other commands get the client)src/app/shared/infrastructure/role.ts+ new home for theRoletype + theletter-composer.component.ts/brief.store.tsimport sites
Steps
- Rewrite
dashboard-view.dto.tsas pure wire shapes (no imports at all); update the adapter'sparse*/toDomainto map wire → domain; run the adapter spec. - Move the
Roletype; update import sites (mechanical; lint will find them). - Refactor change-request-form: UI keeps dispatching to the machine; the submit command
receives/owns the
ApiClient(pattern: othersubmit-*.tscommands). - Add eslint zones:
src/app/**/contracts/**may import nothing from@angular/*, any@*context alias, or relative app code.@shared/infrastructure/api-clientimportable only from**/infrastructure/**(+ the app config/provider and the generated file itself).
- Lint the repo; there must be zero new disables.
Acceptance criteria
dashboard-view.dto.tshas no import statements.- No value import of
@shared/infrastructure/api-clientoutsideinfrastructure/(the only remaining non-infra reference is draft-sync's type-only DTO import, allowed byallowTypeImports). ApiClient confinement is lint-enforced. - Both eslint rules active; planted violations fail lint (contracts→domain, UI→ApiClient value); type-only DTO import still passes.
- Change-request flow works — verified end-to-end in the running app (submit →
command → adapter → backend → reference
BIG-2026-…, success alert); specs green.
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.