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>
138 lines
8.6 KiB
Markdown
138 lines
8.6 KiB
Markdown
# 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.
|