feat(boundaries): WP-03 — contracts purity + ApiClient confinement
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>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Component, computed, inject, input } from '@angular/core';
|
||||
import { Component, computed, input } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonComponent } from '@shared/ui/button/button.component';
|
||||
import { HeadingComponent } from '@shared/ui/heading/heading.component';
|
||||
import { AlertComponent } from '@shared/ui/alert/alert.component';
|
||||
@@ -6,8 +7,7 @@ import { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui
|
||||
import { createStore } from '@shared/application/store';
|
||||
import { whenTag } from '@shared/kernel/fp';
|
||||
import { State, Msg, initial, reduce } from '@registratie/domain/change-request.machine';
|
||||
import { submitChangeRequest } from '@registratie/application/submit-change-request';
|
||||
import { ApiClient } from '@shared/infrastructure/api-client';
|
||||
import { createSubmitChangeRequest } from '@registratie/application/submit-change-request';
|
||||
|
||||
/**
|
||||
* Organism: change-request (adreswijziging) form. Uses the SAME idiom as the
|
||||
@@ -17,7 +17,7 @@ import { ApiClient } from '@shared/infrastructure/api-client';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-change-request-form',
|
||||
imports: [ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent],
|
||||
imports: [FormsModule, ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent],
|
||||
template: `
|
||||
@if (state().tag === 'Submitted') {
|
||||
<app-alert type="ok" i18n="@@changeRequest.success">
|
||||
@@ -50,7 +50,10 @@ import { ApiClient } from '@shared/infrastructure/api-client';
|
||||
`,
|
||||
})
|
||||
export class ChangeRequestFormComponent {
|
||||
private apiClient = inject(ApiClient);
|
||||
// The submit command owns the ApiClient dependency (via the change-request
|
||||
// adapter); the UI holds only this bound command. Field initializer = injection
|
||||
// context, like createStore below.
|
||||
private submit = createSubmitChangeRequest();
|
||||
private store = createStore<State, Msg>(initial, reduce);
|
||||
|
||||
/** Optional seed so Storybook / tests can mount any state directly. */
|
||||
@@ -91,7 +94,7 @@ export class ChangeRequestFormComponent {
|
||||
private async runIfSubmitting() {
|
||||
const s = this.state();
|
||||
if (s.tag !== 'Submitting') return;
|
||||
const r = await submitChangeRequest(this.apiClient, s.data);
|
||||
const r = await this.submit(s.data);
|
||||
if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value });
|
||||
else this.dispatch({ tag: 'SubmitFailed', error: r.error });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user