Files
atomic-design-poc/docs/project/archive/backlog/WP-67-monorepo-behandelportal.md
T
ehoandClaude Opus 5 12f17d9d73 docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every
WP done) and `docs/project/refactor-backlog-setup/` (the arc before it).
Move both under `docs/project/archive/` with `git mv`, so history stays
intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them,
because it points at the now-archived backlog README.

Add `docs/project/archive/README.md`. It states that these trees are
historical and names the two directories that are still live.

Repoint every inbound reference named in RD-30's Files table: CLAUDE.md,
the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the
`document-feature` and `new-ssp` skills, and the readable-codebase PLAN,
README, and RD-19 ticket. Fix two upward-relative links inside the moved
WP files (WP-68, WP-69) that gained a directory level and would otherwise
break. Repoint `.prettierignore`'s two agent-prompt exclusions to their
new path, so prettier keeps leaving those files' exact wording alone.

Mark RD-30 done and check off its acceptance criteria; flip its README
row to done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:00:38 +02:00

138 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.