# 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..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/`. 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.