feat(boundaries): WP-04 — ui ↛ infrastructure + showcase sanction
Move the two wizard lookups behind application-layer facades so ui/ no longer injects infrastructure adapters directly: - RegistratieLookupStore (BRP address + DUO diplomas): owns the resources, runs the trust-boundary parse, exposes adresStatus/prefillAdres/duoLookup. - IntakePolicyStore (scholing threshold): owns the policy resource, exposes the derived threshold. Add the lint rule ui/ + layout/ ↛ **/infrastructure/** (@typescript-eslint variant so it composes with the base direction rules; stories/specs exempted as test scaffolding). Add the documented showcase sanction (may read every context). Fix the docs' inventory: 6 contexts / 5 layers, +brief, +contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { Injectable, computed, inject } from '@angular/core';
|
||||
import { SCHOLING_THRESHOLD_DEFAULT } from '../domain/intake.machine';
|
||||
import { IntakePolicyAdapter } from '../infrastructure/intake-policy.adapter';
|
||||
|
||||
/**
|
||||
* Application-layer facade for the server-owned intake policy (the scholing
|
||||
* threshold config value). It owns the httpResource (created here, in the required
|
||||
* injection context) and exposes the threshold as a derived signal, falling back to
|
||||
* the domain default until the backend answers. The UI reaches the network through
|
||||
* application/, never infrastructure/ directly (CLAUDE.md §1). The backend stays the
|
||||
* authority and re-validates on submit.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IntakePolicyStore {
|
||||
private policy = inject(IntakePolicyAdapter);
|
||||
private policyRes = this.policy.policyResource();
|
||||
|
||||
readonly scholingThreshold = computed(() => this.policyRes.value()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
SCHOLING_THRESHOLD_DEFAULT,
|
||||
} from '@herregistratie/domain/intake.machine';
|
||||
import { createDraftSync } from '@registratie/application/draft-sync';
|
||||
import { IntakePolicyAdapter } from '@herregistratie/infrastructure/intake-policy.adapter';
|
||||
import { IntakePolicyStore } from '@herregistratie/application/intake-policy.store';
|
||||
|
||||
/** Organism: a BRANCHING intake questionnaire. All state lives in one signal
|
||||
driven by the pure `reduce` (intake.machine.ts). Which step renders is derived
|
||||
@@ -120,13 +120,11 @@ import { IntakePolicyAdapter } from '@herregistratie/infrastructure/intake-polic
|
||||
})
|
||||
export class IntakeWizardComponent {
|
||||
private profile = inject(BigProfileStore);
|
||||
private policy = inject(IntakePolicyAdapter);
|
||||
// Server-owned policy (scholing threshold): fetched from the backend via the
|
||||
// application facade, not hardcoded. The backend stays the authority on submit.
|
||||
private policyStore = inject(IntakePolicyStore);
|
||||
private store = createStore<IntakeState, IntakeMsg>(initial, reduce);
|
||||
|
||||
// Server-owned policy: the scholing threshold is fetched from the backend, not
|
||||
// hardcoded. The backend stays the authority and re-validates on submit.
|
||||
private policyRes = this.policy.policyResource();
|
||||
|
||||
/** Optional seed so Storybook / the showcase can mount any state directly. */
|
||||
seed = input<IntakeState>(initial);
|
||||
|
||||
@@ -201,7 +199,7 @@ export class IntakeWizardComponent {
|
||||
// only the policy value; untrack the dispatch (it reads the state signal
|
||||
// internally, which would otherwise make this effect loop on its own write).
|
||||
effect(() => {
|
||||
const scholingThreshold = this.policyRes.value()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT;
|
||||
const scholingThreshold = this.policyStore.scholingThreshold();
|
||||
untracked(() => this.dispatch({ tag: 'SetPolicy', scholingThreshold }));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user