Files
atomic-design-poc/docs/reference/architecture/dependencies.md
T
ehoandClaude Sonnet 5 e7156c5132 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>
2026-08-02 21:01:57 +02:00

76 lines
4.1 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.
# Dependencies & boundaries
How the app's **bounded-context** and **atomic-layer** boundaries are declared, enforced, and
visualized (WP-38, generalized for the two-app monorepo by WP-67). One declarative source —
`.dependency-cruiser.base.js` (a rule _factory_) plus one thin `.dependency-cruiser.<app>.js` per
app — both **guards** the edges and **draws** the graph, replacing the per-context
`no-restricted-imports` blocks that previously had to be hand-copied (and that had left
`herregistratie` uncovered). Each app is cruised **separately**, against its own `tsconfig.json`:
a single merged tsconfig can't resolve both apps' `@auth/*` alias at once (each points at a
different physical directory), so there is no single global config file.
## The rules (single source: `.dependency-cruiser.base.js`)
**Bounded-context direction** — dependencies point inward; every app context may use
`libs/shared` and `libs/beheer`, nothing imports `showcase`:
| Context (apps/ssp) | May import |
| ------------------ | --------------------------------------------------- |
| `auth` | `libs/shared`, `libs/beheer` |
| `registratie` | `libs/shared`, `libs/beheer` |
| `herregistratie` | `registratie`, `libs/shared`, `libs/beheer` |
| `brief` | `libs/shared`, `libs/beheer` |
| `showcase` | everything in `apps/ssp` (sanctioned teaching page) |
| Context (apps/behandelportal) | May import |
| ----------------------------- | ---------------------------- |
| `auth` | `libs/shared`, `libs/beheer` |
| `behandeling` | `libs/shared`, `libs/beheer` |
`libs/shared` is the base (no feature context, no `libs/beheer` — that direction is forbidden too,
so shared never grows a dependency on a peer library). `libs/beheer` is a real bounded context
(admin/stamdata), used identically by both apps — it may depend on `libs/shared`, never the
reverse. An app may not import the other app's source directly.
**Atomic-layer rules:** `domain/` is framework-free (no Angular); `contracts/` import nothing
(pure wire DTOs, ADR-0001); `ui/` + `layout/` never import `infrastructure/` directly (reach data
through an application store/command — type-only DTO imports are fine); the generated `ApiClient`
is a value only inside `infrastructure/` (+ `libs/shared/src/upload`). Plus **no circular**
dependencies. These apply uniformly across an app's tree and both libraries — no debug-state
exception anymore (WP-67 moved the dev panel component out of `libs/shared` into `apps/ssp` since
it's genuinely SSP-specific, coupled to `BigProfileStore`; the shared `ShellComponent` hosts
whichever app-provided component the `DEBUG_PANEL` injection token supplies, or none).
## See the graph
```bash
npm run dep:graph # regenerates docs/reference/architecture/dependency-graph.md (mermaid) — one diagram per app
```
[dependency-graph.md](./dependency-graph.md) is the generated, committed view — contexts × atomic
layers, edges are real imports. It renders on the git host; regenerate + commit after a structural
change.
## Enforce
```bash
npm run dep:check # runs both apps' configs; fails on any forbidden edge; part of `npm run ci` and CI
```
A violation prints the offending `from → to` and the rule name. `dep:check` runs in the local gate
(`scripts/ci-local.sh`) and the `frontend` CI job.
## What still lives in ESLint
Only the non-dependency rules: `@typescript-eslint/no-explicit-any` and the angular-eslint template
accessibility bundle (see `eslint.config.mjs`, scoped to `{apps,libs}/**`). Everything about _who
may import whom_ is in dependency-cruiser.
## Adding a context
Add one context entry to the relevant app's object literal in `.dependency-cruiser.ssp.js` (or
`.dependency-cruiser.behandelportal.js`) — passed straight into the shared `buildConfig` factory in
`.dependency-cruiser.base.js` (and the tsconfig path alias + lazy route) — no more hand-copying
ESLint blocks. The `new-context` skill / `gen:context` (`plopfile.mjs`, ssp-only today) covers the
full checklist.