Files
atomic-design-poc/.claude/skills/new-context/SKILL.md
T
ehoandClaude Opus 4.8 7d2a36ff22
CI / frontend (push) Successful in 2m11s
CI / storybook-a11y (push) Successful in 5m46s
CI / backend (push) Successful in 1m29s
CI / e2e (push) Successful in 2m55s
CI / semgrep (push) Successful in 1m1s
CI / api-client-drift (push) Successful in 2m5s
feat(arch): WP-38 — dependency graph + declarative boundaries (dependency-cruiser)
Adopt dependency-cruiser as the single declarative source for bounded-context +
atomic-layer boundaries, replacing the per-context no-restricted-imports blocks that
had to be hand-copied (and had left herregistratie uncovered). `.dependency-cruiser.js`
encodes context direction (everyone→shared, herregistratie→registratie, showcase→*),
domain-purity, contracts-import-nothing, ui↛infrastructure, ApiClient confinement, and
no-circular. `npm run dep:check` enforces (wired into ci-local.sh + the frontend CI job);
`npm run dep:graph` emits a committed mermaid context×layer graph. ESLint slimmed to
no-explicit-any + template a11y. Docs + new-context skill updated to the single source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:51:04 +02:00

2.3 KiB

name, description
name description
new-context Scaffold a new DDD bounded context (folders, path alias, boundary rules, lazy route). Use when adding a new business capability that doesn't belong in an existing context.

New bounded context

A context is a business capability, not a user group — a user group is an actor that may span contexts (ADR-0002). Check first whether the capability belongs in an existing context; new contexts are rare.

Naming: domain contexts are Dutch (registratie, herregistratie); only shared/reusable code is English. The context name is the ubiquitous language term.

Steps

  1. Folders — src/app/<ctx>/{domain,application,infrastructure,ui} (contracts/ only once it gets a wire seam). Empty layers can wait; don't scaffold placeholders.

  2. Path alias — add "@<ctx>/*": ["src/app/<ctx>/*"] to tsconfig.json paths. Aliases are direction statements; always import cross-context via the alias.

  3. Boundaries (.dependency-cruiser.js, WP-38 — the single declarative source; dependencies point inward and toward shared only). Add ONE contextRule(...) entry for the new context listing the contexts it may not import (copy the brief leaf example), and add the new context to the forbidden list of any context that must not depend on it. The layer rules (domain/ framework-free, contracts/ import-nothing, ApiClient confinement, ui ↛ infrastructure) match by glob and cover it automatically. Verify with npm run dep:check; regenerate the graph with npm run dep:graph. (Boundaries are no longer in eslint.config.mjs — that now holds only no-explicit-any + template a11y.)

  4. Route — lazy child under the persistent shell in app.routes.ts:

    { path: '<ctx>', canActivate: [authGuard],
      loadComponent: () => import('@<ctx>/ui/<ctx>.page').then(m => m.CtxPage) }
    
  5. Build the first feature slice with the new-feature skill.

Worked example

src/app/brief/ — an independent leaf context (depends only on shared): see its folder layout and its contextRule entry in .dependency-cruiser.js.

Verify

npm run dep:check && npm run lint && npm run build
# prove the fence works: add a forbidden import (e.g. new ctx → @herregistratie/*),
# confirm `npm run dep:check` fails, remove it.