# WP-67 — Merge behandelportal into this repo as a monorepo Status: done Phase: 11 — Behandelportal ## Why WP-61 bootstrapped `behandelportal` as a **separate sibling repo**, per ADR-0002's original "separate frontend application" reading taken literally as "separate git repository." That produced real, measured friction: a hand-vendored, manually-kept-in-sync copy of the backend's OpenAPI doc instead of a live-generated one; `shared/ui`+`shared/layout` forked at WP-61 and already silently diverging by the time this WP checked (7 files differed); a `beheer` (admin/stamdata) context and the `styles.scss` token bridge duplicated byte-for-byte across both repos; a second CI/lint/CLAUDE.md to hand-maintain. The user asked to collapse this into one repo so the two apps share one CI, one shared UI library, and one generated API client — the standard monorepo payoff, now that a second real frontend exists. ## Read first - [ADR-0002](../reference/architecture/0002-user-groups-and-bounded-contexts.md) — its "Amendment (WP-67)" section records exactly what changed and why the underlying actor/bounded-context reasoning didn't. - [dependencies.md](../reference/architecture/dependencies.md) — the per-app dependency-cruiser split this WP introduced. - `behandelportal-bootstrap` memory (prior sessions) for WP-61/62's own decisions. ## Decisions (made during this WP, not pre-made — see the plan file for the questions asked) - **Unify** `libs/shared` into one library both apps import (not two copies) — the user's explicit call, given the two trees had already started diverging. - Restructure into **`apps/ssp/` + `apps/behandelportal/`** (not an Angular-CLI `projects/` addition next to an untouched `src/app/`) — the user's call, accepting the larger diff. - **Leave** the old sibling repo (`/home/eho/repos/behandelportal`) untouched — no deletion. - Bring the content in as a **fresh commit**, no git-history import — that repo is itself a fork of this repo's own pre-WP-61 history, so a subtree merge would fight to reconcile two copies of the same ancestor commits for one commit's worth of real new content. - **Course corrections found during execution** (squarely within "unify shared," not separately asked): `libs/beheer` (admin/stamdata — already identical in both apps, not actor-specific) and `libs/shared/styles.scss` (the token bridge, also byte-identical) were folded in alongside `libs/shared/ui`. `auth` was deliberately **not** unified — ADR-0002 models it as actor-specific (different `Principal` variants), so today's accidental similarity is expected to diverge, not something to force together. - **Two Storybook instances, not one** (`.storybook-ssp/`, `.storybook-behandelportal/**`) — a structural necessity, not a simplification choice: both apps' `auth` (and other) context aliases share the name `@auth/*` but resolve to different physical directories, so no single tsconfig can compile both apps' stories in one pass. - Each app's own `shell/nav.config.ts` supplies its primary nav + admin links to the shared `SiteHeaderComponent` via two new injection tokens (`HEADER_NAV_ITEMS`, `HEADER_ADMIN_LINKS`) rather than the component hardcoding one app's routes — the same "shared component takes copy as an input, the domain caller supplies it" idiom CLAUDE.md already used for `shared/ui` copy, extended to injection tokens for this DI-shaped case. The dev-only "state" panel (`DebugStateComponent`) moved out of `libs/shared` into `apps/ssp` entirely (it's coupled to `BigProfileStore`, an ssp-only store) and is now hosted by the shared `ShellComponent` via a `DEBUG_PANEL` injection token — no provider, no panel. ## Files Nearly the whole repo, mechanically (`git mv`), plus: - `angular.json` — rewritten for 4 projects: `ssp`, `behandelportal` (real apps), `shared`, `beheer` (library projects whose only real purpose is giving `ng test` a `buildTarget` to satisfy — see `libs/*/src/test-entry.ts`'s comment). - `tsconfig.json` (root, no more `paths` — see its comment) + a new `tsconfig.json` per app/library declaring that project's own full alias map. - `.dependency-cruiser.base.js` (rule factory) + `.dependency-cruiser.ssp.js` + `.dependency-cruiser.behandelportal.js` (replacing the single `.dependency-cruiser.js`). - `eslint.config.mjs`, `package.json` scripts, `docker-compose.yml`, `.github/workflows/ci.yml`, `scripts/ci-local.sh`, `scripts/check-tokens.sh`, `scripts/gen-snippets.mjs`, `scripts/serve-i18n.mjs`, `scripts/dep-graph.sh`, `plopfile.mjs`, `nswag.json` — all re-pointed at the new paths / made per-app aware. - `CLAUDE.md`, `ARCHITECTURE.md`, `dependencies.md`, ADR-0002 — updated for the new layout. ## Steps 1. Branch, then `git mv` the SSP's `src/app/*` (minus `shared/`) → `apps/ssp/src/app/`, plus `main.ts`/`index.html`/`locale/`/`proxy.conf.json`/`tsconfig.app.json`/`tsconfig.spec.json`. 2. `git mv src/app/shared` → `libs/shared/src`; `git mv src/app/beheer` → `libs/beheer/src`; fold in `environments/` and the Storybook `docs/*.mdx` (both found byte-identical between the two repos — same treatment as `shared/ui`). 3. Copy (not `git mv` — a different repo) behandelportal's `auth`/`behandeling` + `main.ts`/`index.html`/`locale/` into `apps/behandelportal/src/`. 4. Rewrite `angular.json` for the two real projects + two library test-only projects; fix every relative import that broke (`environments/environment` → `@shared/environments/environment`; MDX docs' `../app/shared/...` → `../src/...`). 5. Split `.dependency-cruiser.js` into a base factory + one config per app; broaden `eslint.config.mjs`'s `files` glob. 6. Rewrite `package.json` scripts, `docker-compose.yml` (added a `web-behandelportal` service), `.github/workflows/ci.yml` + `scripts/ci-local.sh` (both apps built/tested, path filters widened), the four `scripts/*.mjs`/`*.sh` helpers, `plopfile.mjs`'s three generators (value-object/form-machine paths now resolve `shared`/`beheer` to `libs/`, the `context` generator's tsconfig/dep-cruiser/routes edits re-anchored on the ssp config). 7. Split `.storybook/` into `.storybook-ssp/` + `.storybook-behandelportal/` (forced by the `@auth/*` alias collision); moved the CIBG token bridge (`styles.scss`) and the generated `documentation.json` to avoid two more collisions. 8. Add `HEADER_NAV_ITEMS`/`HEADER_ADMIN_LINKS`/`DEBUG_PANEL` injection tokens to `SiteHeaderComponent`/`ShellComponent`; give each app its own `shell/nav.config.ts` and (ssp only) `shell/debug-state/`. 9. Update `CLAUDE.md`, `ARCHITECTURE.md`, `dependencies.md`, and amend ADR-0002. ## Acceptance criteria - [x] `npm run ci` green (lint, dep:check ×2, format:check, check:tokens, test:coverage ×4 projects, `ng build --localize` ×2, npm audit, backend test, snippets drift, api-client drift). - [x] Both apps' dev servers run against the one shared backend (`npm start` / `npm run start:behandelportal`, ports 4200/4201, backend on 5000). - [x] Both apps' production + localized builds succeed. - [x] Both Storybook instances build (`build-storybook[:behandelportal]`). - [x] `npm run gen:api` generates the one client into `libs/shared` with zero drift. - [x] `docker compose up` serves both apps + the shared backend. ## Verification `npm run ci`; `npx ng build ssp --localize && npx ng build behandelportal --localize`; `npx ng run ssp:build-storybook && npx ng run behandelportal:build-storybook`; manual: `npm start` on :4200 and `npm run start:behandelportal` on :4201 against `dotnet run --project backend/src/BigRegister.Api`, log in on both. ## Out of scope - Deleting or archiving the old sibling repo (`/home/eho/repos/behandelportal`) — left untouched per the user's explicit choice. - `scripts/create-frontend.mjs` / the `new-ssp` skill (bootstrapping a _third_ portal as a fresh standalone repo) — whether future portals should also join this monorepo is a separate decision. - Extending the `storybook-a11y` CI job to behandelportal's own Storybook instance — it still only covers ssp's, unchanged from before this WP. - Reconciling `auth` between the two apps — left duplicated, deliberately (see Decisions). ## Risks The `libs/shared`/`libs/beheer` "library" Angular projects exist solely to give the unit-test builder a `buildTarget` to resolve (`@angular/build:unit-test` always requires one, even for a project that's only ever tested) — `test-entry.ts` + `tsconfig.app.json` in each are a `ponytail:`-flagged workaround, not a real buildable/publishable library. If either library ever needs to actually build (e.g. an ng-packagr distributable), replace this with a real library target then.