docs: showcase roadmap (Storybook-as-curriculum, FP primitives, enforcement)
Prioritised backlog from a three-part analysis of the atomic-design + FP showcase: concrete P1-P3 items per track with file paths, teaching value, and effort. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
96
docs/SHOWCASE-ROADMAP.md
Normal file
96
docs/SHOWCASE-ROADMAP.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Showcase roadmap
|
||||||
|
|
||||||
|
Prioritised backlog for making this POC a stronger teaching showcase for **atomic
|
||||||
|
design + FP in the UI** — optimising for maintainability, speed, and low cognitive load
|
||||||
|
for junior *and* senior developers.
|
||||||
|
|
||||||
|
## How to read this
|
||||||
|
|
||||||
|
Each track below is a table of concrete items: **Item · Where · Why · Effort · Priority**.
|
||||||
|
Nothing here is built yet — pick items off the top. Priorities: **P1** = highest
|
||||||
|
teaching-value-per-effort, do first; **P2** = strengthens the showcase; **P3** = backlog.
|
||||||
|
Effort is a rough solo estimate.
|
||||||
|
|
||||||
|
This roadmap is deliberately scoped to three tracks (Storybook-as-curriculum, FP
|
||||||
|
primitives, Enforcement depth). A fourth track — onboarding docs (GETTING-STARTED,
|
||||||
|
GLOSSARY, layer-dependency diagram, ADR index) — was identified but de-scoped for now.
|
||||||
|
|
||||||
|
### Current strengths (what NOT to touch)
|
||||||
|
|
||||||
|
The foundations are already exemplary and should be preserved as-is:
|
||||||
|
|
||||||
|
- **Pure reducers / state machines** — `*.machine.ts` model illegal states away with
|
||||||
|
tagged unions; effects live in commands, never in `reduce`.
|
||||||
|
- **`RemoteData<E,T>`** (`src/app/shared/application/remote-data.ts`) — `map`/`map2`/
|
||||||
|
`map3`/`andThen`/`fromResource`/`foldRemote`, rendered via the `<app-async>` molecule.
|
||||||
|
- **Parse-don't-validate value objects** (`src/app/registratie/domain/value-objects/`) —
|
||||||
|
branded types built only through a `Result`-returning parser.
|
||||||
|
- **Anti-corruption parsers** at every DTO seam (`infrastructure/*.adapter.ts`).
|
||||||
|
- **ESLint-enforced layer boundaries** (`eslint.config.mjs`): no `any`, `domain/` imports
|
||||||
|
no Angular, cross-context direction locked.
|
||||||
|
- **Storybook** — 96% story coverage, a11y addon + docs addon on, titled `Layer/Name`.
|
||||||
|
- **Token linting** (`npm run check:tokens`) blocks hardcoded colours.
|
||||||
|
|
||||||
|
The gaps below are about *teaching reach*, not correctness.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Track A — Storybook as a curriculum
|
||||||
|
|
||||||
|
Storybook is the showcase vehicle, but today it renders isolated components with no
|
||||||
|
narrative. `.storybook/main.ts` already globs `../src/**/*.mdx` and loads
|
||||||
|
`@storybook/addon-docs`, so MDX pages drop in with zero config.
|
||||||
|
|
||||||
|
| Item | Where | Why (teaching value / cognitive load) | Effort | Prio |
|
||||||
|
|------|-------|----------------------------------------|--------|------|
|
||||||
|
| **Atomic-design primer (MDX)** | `src/docs/atomic-design.mdx` | The pyramid + the "each layer imports only below" rule, with a *live* composition chain of real components: `button` (atom) → `form-field` (molecule) → `document-upload` (organism) → `page-shell` (template). Links to the ESLint rules that enforce it. Gives newcomers the mental model before they read code. | 2–3h | P1 |
|
||||||
|
| **Design-token gallery (MDX)** | `src/docs/design-tokens.mdx` | RHC colours, the `--rhc-space-max-*` spacing scale (with resolved px), typography; plus when to use a raw `--rhc-*` token vs. an `--app-*` wrapper from `src/styles.scss`. Answers the senior's "what's the actual value of `--rhc-space-max-md`?" and ties to `check:tokens`. | 2h | P1 |
|
||||||
|
| **FP-in-the-UI primer (MDX)** | `src/docs/fp-in-ui.mdx` | A Storybook front door to the concepts already in `src/app/showcase/concepts.page.ts` and `docs/fp-tea-atomic-design.md` (RemoteData, machine, parse-don't-validate) so the component library and the FP guide cross-link instead of living apart. | 1.5h | P1 |
|
||||||
|
| **`argTypes` / controls** | high-traffic atom stories: `button`, `text-input`, `radio-group`, `status-badge`, `alert` (`src/app/shared/ui/*/*.stories.ts`) | Lets devs explore every variant from the Controls panel instead of editing story code. Fastest "play with it" path for juniors. | 1.5h | P2 |
|
||||||
|
| **Anatomy story for `async`** | `src/app/shared/ui/async/async.stories.ts` | Show all four states (Loading / Empty / Failure / Success) in one view — it teaches the exhaustive fold but doesn't currently *display* it exhaustively. | 45m | P2 |
|
||||||
|
| **`play` / interaction tests** | 2–3 stories (button disabled-during-submit; `form-field` error announced via `role="alert"`; `async` retry calls `reload()`) | Demonstrates behaviour, not just appearance, and doubles as regression cover. Feeds Track C's CI gate. | 2h | P2 |
|
||||||
|
| **Shell story** | `src/app/shared/layout/shell/shell.stories.ts` | The only component with no story; a canvas showing the persistent header/footer completes the library. | 30m | P3 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Track B — FP primitives
|
||||||
|
|
||||||
|
The pure core is excellent but under-demonstrates a few composition tools; learners
|
||||||
|
currently hand-branch and reinvent them.
|
||||||
|
|
||||||
|
| Item | Where | Why (teaching value / cognitive load) | Effort | Prio |
|
||||||
|
|------|-------|----------------------------------------|--------|------|
|
||||||
|
| **`Result` combinators** | `src/app/shared/kernel/fp.ts` (+ new `fp.spec.ts`) | Add `map`, `mapErr`, `andThen`, `fold`, `getOrElse`. Today only direct `r.ok ? … : …` branching exists — fine for one step, painful across a pipeline. **Biggest single FP gap.** Then refactor **one** real call site to show `parse ▸ mapErr(localize) ▸ map(toDomain)`. | 1.5h | P1 |
|
||||||
|
| **Value-object specs** | `postcode.spec.ts`, `uren.spec.ts`, `big-nummer.spec.ts` next to `src/app/registratie/domain/value-objects/*.ts` | The repo's own rule is "domain must have a spec," yet only `email` does. Cover normalisation (`"1234AB"`→`"1234 AB"`), boundaries, and invalid inputs. Closing this makes the rule credible and teaches value-object testing. | 1h | P1 |
|
||||||
|
| **Property-based test example** | one spec (e.g. Postcode normalisation idempotence, or the `RemoteData.map` functor-composition law); adds `fast-check` dev-dep | Every current test is example-based. One law-based test shows how to catch edge cases you didn't enumerate. **Flag:** introduces a new dev dependency — justified because property testing is an explicit teaching goal. | 1h | P2 |
|
||||||
|
| **`NonEmptyArray<T>` helper** | `src/app/shared/kernel/fp.ts`; use for `vraagIds` / required upload categories | Makes "at least one" a *type* rather than a runtime check — a crisp "make illegal states unrepresentable" lesson applied to collections. | 1h | P3 |
|
||||||
|
| **Effect/command testability (note)** | `src/app/registratie/application/{draft-sync,submit-*}.ts` | Direction only, not built here: inject the HTTP effect so commands are unit-testable without a full component. Documents how to close the one untested layer. | — | P3 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Track C — Enforcement depth
|
||||||
|
|
||||||
|
ESLint checks imports; several documented rules are otherwise only hoped-for. Turning
|
||||||
|
them into machine-checked guarantees is itself a teachable "fitness function" pattern.
|
||||||
|
|
||||||
|
| Item | Where | Why (teaching value / cognitive load) | Effort | Prio |
|
||||||
|
|------|-------|----------------------------------------|--------|------|
|
||||||
|
| **Architecture-fitness test** | `src/architecture.spec.ts` + `npm run check:architecture`, wired into `.github/workflows/ci.yml` | A vitest that scans `domain/` for Angular/HTTP/RxJS imports and asserts cross-context direction. Belt-and-suspenders over ESLint, and a worked example of an executable architecture rule. | 2h | P1 |
|
||||||
|
| **a11y as a CI gate** | Storybook test-runner + axe (or vitest + axe on rendered atoms), added to `ci.yml` | The a11y addon's checks are visible in the panel but not enforced. Gating them turns "we care about a11y" into a guarantee — high value for a design-system showcase. | 2–3h | P1 |
|
||||||
|
| **Interaction tests in CI** | run Track-A `play` tests via the Storybook test-runner in `ci.yml` | Makes behavioural stories part of the pipeline, not just docs. | 1h | P2 |
|
||||||
|
| **Domain coverage gate** | vitest coverage threshold on `domain/**` | Guarantees the "domain must have a spec" rule holds as the code grows. | 45m | P3 |
|
||||||
|
| **Dependency-graph visual** | `madge` script producing an SVG/JSON of the layer graph | A generated picture makes the architecture graspable at a glance and catches accidental edges. | 45m | P3 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Suggested sequencing
|
||||||
|
|
||||||
|
Do P1 across tracks, interleaved so each shows value early:
|
||||||
|
|
||||||
|
1. **[B]** `Result` combinators + value-object specs — small, self-contained, immediate.
|
||||||
|
2. **[A]** the three MDX pages — the visible "showcase" upgrade.
|
||||||
|
3. **[C]** architecture-fitness test + a11y-in-CI — locks the guarantees in.
|
||||||
|
|
||||||
|
Then P2 (controls, anatomy/interaction stories, property test, interaction tests in CI).
|
||||||
|
P3 items are backlog. None of this changes runtime behaviour or the public component
|
||||||
|
API; it is additive teaching + enforcement.
|
||||||
Reference in New Issue
Block a user