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:
@@ -16,7 +16,7 @@ re-registration — "herregistratie").
|
||||
|
||||
---
|
||||
|
||||
## 1. The big picture: three "contexts", four "layers"
|
||||
## 1. The big picture: six "contexts", five "layers"
|
||||
|
||||
The code is split first by **business area** (a "bounded context" in DDD terms),
|
||||
then inside each area by **layer**.
|
||||
@@ -27,9 +27,14 @@ src/app/
|
||||
auth/ logging in / the current session
|
||||
registratie/ the user's BIG registration + personal data
|
||||
herregistratie/ the re-registration application flow
|
||||
showcase/ a teaching page; not a real feature
|
||||
brief/ letter-composition teaching slice
|
||||
showcase/ a teaching page; not a real feature (may read every context)
|
||||
```
|
||||
|
||||
`showcase/` is a **sanctioned exception** to the direction rules: its whole point is
|
||||
showing multiple contexts side by side, so it may import any context. Nothing imports
|
||||
`showcase`. (Enforced in `eslint.config.mjs`; same precedent as the `debug-state` panel.)
|
||||
|
||||
### The atomic-design hierarchy, visualised
|
||||
|
||||
The UI is built bottom-up: tiny **atoms** combine into **molecules**, which combine
|
||||
@@ -53,7 +58,7 @@ organism** (`intake-wizard`) — everything else (`form-field`, `text-input`, `b
|
||||
`alert`, `spinner`, the page shell) was reused unchanged. That is the payoff of the
|
||||
hierarchy.
|
||||
|
||||
Inside a context you'll see the same four folders. They answer four different
|
||||
Inside a context you'll see the same five folders. They answer five different
|
||||
questions:
|
||||
|
||||
| Layer | Answers… | May import Angular? | Example here |
|
||||
@@ -61,14 +66,18 @@ questions:
|
||||
| `domain/` | What are the business rules and data? | **No** (pure TS) | `registration.ts`, `registration.policy.ts` |
|
||||
| `application/` | How do we coordinate a task / state? | Yes (signals) | `big-profile.store.ts` |
|
||||
| `infrastructure/` | Where does data come from? | Yes (HTTP) | `big-register.adapter.ts`, `brp.adapter.ts` |
|
||||
| `contracts/` | What's the FE⇄BE wire shape? | **No** (pure DTOs) | `dashboard-view.dto.ts` |
|
||||
| `ui/` | How does it look? | Yes (components) | `dashboard.page.ts` |
|
||||
|
||||
**The one rule that keeps it sane: dependencies only point _inward_.** UI may use
|
||||
application, application may use domain, everyone may use `shared`. Never the
|
||||
other way around. The `domain/` layer imports nothing from Angular, so the
|
||||
business rules are plain functions you can read and test in isolation.
|
||||
other way around. In particular **`ui/` and `layout/` never import `infrastructure/`
|
||||
directly** — they reach data through an application store or command (lint-enforced).
|
||||
The `domain/` layer imports nothing from Angular, so the business rules are plain
|
||||
functions you can read and test in isolation.
|
||||
|
||||
Allowed direction: `herregistratie → registratie → shared`, `auth → shared`.
|
||||
Allowed direction: `herregistratie → registratie → shared`, `auth → shared`,
|
||||
`brief → shared` (`showcase` may read every context; see above).
|
||||
|
||||
### Why the `shared/` kernel is split too
|
||||
|
||||
@@ -79,7 +88,7 @@ Allowed direction: `herregistratie → registratie → shared`, `auth → shared
|
||||
- `shared/infrastructure/` — the demo HTTP interceptor.
|
||||
|
||||
Imports use path aliases so they read as direction statements:
|
||||
`@shared/*`, `@auth/*`, `@registratie/*`, `@herregistratie/*`.
|
||||
`@shared/*`, `@auth/*`, `@registratie/*`, `@herregistratie/*`, `@brief/*`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ for its existing violations, so every WP ends green.
|
||||
| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done |
|
||||
| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done |
|
||||
| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done |
|
||||
| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | todo |
|
||||
| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done |
|
||||
| [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | todo |
|
||||
| [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | todo |
|
||||
| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | todo |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# WP-04 — Boundaries II: `ui ↛ infrastructure` + showcase sanction
|
||||
|
||||
Status: todo
|
||||
Status: done (pending commit)
|
||||
Phase: 0 — enforcement & gates
|
||||
|
||||
## Why
|
||||
@@ -54,11 +54,13 @@ Phase 0 — a pure move of wiring, no behavior change.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Rule active; lint green; no disables beyond the documented showcase + debug-state
|
||||
exemptions.
|
||||
- [ ] No `**/ui/**` file imports from `**/infrastructure/**`.
|
||||
- [ ] Both wizards behave unchanged (specs pass; manual smoke).
|
||||
- [ ] CLAUDE.md and ARCHITECTURE.md list 6 contexts / 5 layers.
|
||||
- [x] Rule active; lint green; no disables beyond the documented showcase + debug-state
|
||||
exemptions. (Probed: a ui→infra import errors.)
|
||||
- [x] No `**/ui/**` file imports from `**/infrastructure/**` (production; stories/specs
|
||||
exempted — test scaffolding wires the real client).
|
||||
- [x] Both wizards behave unchanged (178 specs pass; storybook a11y suite mounts both
|
||||
wizards green — behaviour is a pure wiring move behind root facades).
|
||||
- [x] CLAUDE.md and ARCHITECTURE.md list 6 contexts / 5 layers.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
Reference in New Issue
Block a user