feat(WP-67): merge behandelportal into this repo as a monorepo

Restructures into apps/ssp + apps/behandelportal (two Angular projects)
plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's
separate sibling repo. That split had already produced real drift: a
hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree
forked and silently diverging (7 files), and beheer + the styles.scss
token bridge duplicated byte-for-byte across both repos.

- git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/,
  environments/, the Storybook docs/*.mdx, and styles.scss into
  libs/shared + libs/beheer (all confirmed identical between the two
  repos before merging). auth stays deliberately duplicated per
  ADR-0002 (actor-specific, expected to diverge) - amended there.
- One generated API client (libs/shared), no more vendored swagger.json.
- .dependency-cruiser split into a base factory + one config per app,
  and Storybook into .storybook-ssp/.storybook-behandelportal - both
  forced by the @auth/* alias resolving to different directories per app.
- SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/
  HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies
  its own nav/admin-links/dev-panel instead of one being hardcoded.
- CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated;
  WP-67 backlog entry documents the full decision trail.

npm run ci green (lint, dep:check x2, 360 tests across ssp/
behandelportal/shared/beheer, both localized builds, backend tests,
snippet + api-client drift); both dev servers, both Storybook
instances, and docker compose verified working.

The old sibling repo (/home/eho/repos/behandelportal) is left
untouched, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-02 21:01:57 +02:00
co-authored by Claude Sonnet 5
parent d3f3b13345
commit e7156c5132
403 changed files with 7103 additions and 60917 deletions
@@ -21,7 +21,9 @@ Getting this wrong is expensive: split the code by role and every feature smears
Confirmed constraints (with the product owner):
- The backoffice is a **separate frontend application**, own audience, own deployment.
- The backoffice is a **separate frontend application** (own audience, own deployable —
see the [WP-67 amendment](#amendment-wp-67-2026-08-01-one-repo-not-two) below for where
its source actually lives).
- The groups **authenticate differently**: Zorgverlener via DigiD/BSN; Behandelaar via employee SSO.
- Both act on the **same underlying aggregate** — the aanvraag/registration — but see different views.
@@ -43,9 +45,11 @@ Confirmed constraints (with the product owner):
The same real-world thing is described in two different languages:
- **Zelfbediening (SSP)** — the Zorgverlener: _"ik vraag herregistratie aan"_ — eligibility, fill in
my data, upload documents, submit. **This repo.**
my data, upload documents, submit. **`apps/ssp` in this repo** (was "this repo" itself
before WP-67 turned it into a monorepo).
- **Behandeling (backoffice)** — the Behandelaar: _"ik beoordeel de aanvraag"_ — werkvoorraad,
beoordeling, besluit, meer-info-opvragen, SLA, audit. **A sibling application**, not a folder here.
beoordeling, besluit, meer-info-opvragen, SLA, audit. **`apps/behandelportal`** — a
separate Angular _project_, not a separate _repo_ (see the amendment below).
Diverging verbs over the same noun is the textbook signal for **two bounded contexts**.
@@ -60,16 +64,16 @@ contract_ between the two contexts:
Ingediend → In behandeling → (Meer info gevraagd ⇄) → Goedgekeurd / Afgewezen
```
The Behandeling context **advances** this lifecycle; the SSP **reads** it. Today the SSP already holds
the seed of it — `pendingHerregistratie` in `big-profile.store.ts:53` is the first, coarsest read of
that status ("in behandeling"). As the backoffice appears, that single boolean grows into a real
status the backend publishes.
The Behandeling context **advances** this lifecycle; the SSP **reads** it. WP-63 published the
full lifecycle enum backend-side (`AanvraagStatusTag`); the SSP's dashboard `pendingHerregistratie`
signal (`big-profile.store.ts`) turned out to be a pure client-side optimistic flag, not a read of
any backend field — WP-65 is where a behandelaar action first reaches `Ingediend`/`MeerInfoGevraagd`.
```mermaid
graph TD
subgraph FE["Frontend bounded contexts (separate apps)"]
SSP["<b>Zelfbediening (SSP)</b><br/>Zorgverlener · DigiD/BSN<br/><i>this repo</i>"]
BO["<b>Behandeling (backoffice)</b><br/>Behandelaar · employee SSO<br/><i>sibling app</i>"]
subgraph FE["Frontend bounded contexts (two Angular projects, one repo — WP-67)"]
SSP["<b>Zelfbediening (SSP)</b><br/>Zorgverlener · DigiD/BSN<br/><i>apps/ssp</i>"]
BO["<b>Behandeling (backoffice)</b><br/>Behandelaar · employee SSO<br/><i>apps/behandelportal</i>"]
end
BE["<b>Backend domain</b><br/>aanvraag aggregate (system of record)<br/>status lifecycle · authorization"]
SSP -- "reads aanvraag status<br/>(decision DTOs, ADR-0001)" --> BE
@@ -115,14 +119,45 @@ not merely a new login.
## Consequences
- This repo **stays the pure SSP**. No backoffice code leaks in; no role-named folders appear.
- The backoffice ships as a **separate app** against the same backend and the same OpenAPI contract.
- `apps/ssp` **stays the pure SSP**. No backoffice code leaks in; no role-named folders appear.
- The backoffice ships as a **separate Angular project** (`apps/behandelportal`, WP-67 —
originally a separate repo, see the amendment below) against the same backend and the same
OpenAPI contract.
- The one concrete FE change when actor #2 lands is `Session → Principal` in the `auth` context; the
`authGuard`/`SessionStore` seams already localise that (`auth.guard.ts`, `session.store.ts`).
- The backend becomes the authority for the **aanvraag status lifecycle** and for **authorization**,
publishing both as decision DTOs — a natural extension of ADR-0001, not a new pattern.
- `pendingHerregistratie` is understood as a _temporary stand-in_ for a real, backend-owned status.
## Amendment (WP-67, 2026-08-01): one repo, not two
WP-61 initially built `apps/behandelportal` as a **separate sibling repo**
(`/home/eho/repos/behandelportal`), taking this ADR's "separate frontend application" literally
as "separate git repository." That produced real friction WP-67 then undid: a hand-vendored,
manually-kept-in-sync copy of the backend's OpenAPI doc instead of a live-generated one, a
`shared/ui`+`shared/layout` tree forked at WP-61 and already silently diverging by the time
WP-67 checked (7 files), a `beheer` (admin/stamdata) context and `styles.scss` token bridge
duplicated byte-for-byte across both repos, and a second CI/lint/CLAUDE.md to hand-maintain.
**The bounded-context reasoning above is unchanged** — it never depended on repo count. What
changes is purely the _packaging_:
- Two Angular CLI projects in one workspace: `apps/ssp`, `apps/behandelportal` — each still
its own deployable, its own `angular.json` build/serve/test targets, its own port.
- `libs/shared` (design system + kernel + the one generated API client) and `libs/beheer`
(admin/stamdata — genuinely identical for both apps, not actor-specific) are cross-app
libraries. `auth` stays **duplicated**, not unified — per §3 above, it's expected to diverge
(Zorgverlener DigiD/BSN vs. Behandelaar employee SSO), so unifying it now would be forcing
today's accidental similarity into a shape that fights tomorrow's real difference.
- One backend, one OpenAPI doc, one generated client — the vendored-swagger workaround is
gone; `npm run gen:api` regenerates the live doc straight into `libs/shared`.
- Each app still needs its **own** Storybook instance (`.storybook-ssp/`,
`.storybook-behandelportal/`) — `@auth/*` (and other context aliases) resolve to different
physical directories per app, so one merged tsconfig can't serve both at once. This is a
real, structural constraint, not a leftover of the old two-repo split.
- The old sibling repo was left untouched (not deleted) when this migration landed — a
redundant clone, safe to archive once the monorepo version is verified in daily use.
## Out of scope here (next steps, not built)
- Building the Behandeling backoffice application.