feat: minimal signals + RemoteData template with a users feature
Extracted from atomic-design-poc: RemoteData<E,T> (bare union + fromResource, no combinators) + a trimmed <app-async> switch, no Elm-style store — state is resource() plus one plain signal. Minimal DDD layering per context (domain/infrastructure/application/ui) combined with atomic design inside ui/ (atoms/molecules/organisms/templates/pages), mirroring the POC's conventions at template scale. One worked feature (users/): a list with a click-through to a detail view (its own independent resource() fetch) and a Back action. Tests are BDD-style (describe/it, one assertion per it) and black-box (assert rendered DOM/emitted events only) - 100% branch/line coverage. README.md documents what's deliberately omitted vs. the POC and the concrete growth path back to it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,59 +1,89 @@
|
||||
# NgSignalsTemplate
|
||||
# ng-signals-template
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 22.1.2.
|
||||
A minimal Angular 22 starter: signals for state, `resource()` for async data, and a
|
||||
`RemoteData` type that makes "loading but has an error," "success with no value," and
|
||||
similar illegal combinations impossible to construct. One worked feature (`users/`) shows
|
||||
the whole pattern end to end, including an action (click a user → see their details →
|
||||
go back).
|
||||
|
||||
## Development server
|
||||
It's extracted from a larger reference app — the "POC" referenced throughout this
|
||||
document — which shows the same ideas grown up to production scale (multi-context DDD,
|
||||
enforced architecture boundaries, i18n, a real design system, generated API clients). This
|
||||
template deliberately keeps only the part of that setup useful from day one, and documents
|
||||
exactly where to reach for the rest as a project grows.
|
||||
|
||||
To start a local development server, run:
|
||||
## Running it
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
npm start # ng serve
|
||||
npm test # ng test (Vitest)
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
## What's here
|
||||
|
||||
## Code scaffolding
|
||||
- **`RemoteData<E, T>`** (`shared/application/remote-data.ts`) — a 4-variant union
|
||||
(`Loading | Empty | Failure | Success`) plus `fromResource()`, which projects Angular's
|
||||
own `resource()` into one. No store, no reducer — `resource()` already holds the async
|
||||
state; `RemoteData` just normalizes it for exhaustive rendering.
|
||||
- **`<app-async>`** (`shared/ui/molecules/async.component.ts`) — a `@switch` over all 4
|
||||
states: a spinner while loading, an empty message, a failure message with a retry
|
||||
button, or your projected content on success. Reused by both fetches in `users/`.
|
||||
- **`<app-page-shell>`** (`shared/ui/templates/page-shell.component.ts`) — a heading plus
|
||||
one content slot. That's it.
|
||||
- **`users/`** — one feature context, laid out the same way a bigger one would be:
|
||||
`domain/` (pure types, no Angular import), `infrastructure/` (the only file allowed to
|
||||
call `fetch`), `application/` (composes infrastructure + `resource()` — this is also
|
||||
exactly where a real store would slot in later), `ui/` (organisms + the page). Clicking
|
||||
a user in the list sets one plain `signal` on the page, which swaps in a
|
||||
`UserDetailComponent` that does its own independent fetch.
|
||||
- Path aliases `@shared/*` and `@users/*` (see `tsconfig.json`) instead of relative
|
||||
`../../` imports, one per context — add one per new context you create.
|
||||
- Tests are BDD-style (`describe`/`it`, one `expect` per `it`) and black-box: they assert
|
||||
on rendered DOM and emitted events, never on a component's private fields, so a test
|
||||
never breaks just because an internal was refactored.
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
## What's deliberately not here (vs. the POC)
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
| Missing | Why |
|
||||
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| Elm-style store (`createStore`/Model-Msg-reduce) | Not needed until a page's state has more than a couple of interacting fields — see Growth path below. |
|
||||
| i18n (`$localize` + translation file) | POC-specific requirement (a Dutch app shipping English too); irrelevant for a single-locale starter. |
|
||||
| CIBG Huisstijl theming / token bridge | The POC's specific design system; a starter has no house style to vendor yet. |
|
||||
| `dependency-cruiser` boundary enforcement | Real value once you have 2+ contexts that must not import each other; overhead for one. |
|
||||
| `contracts/` layer + generated API client + `parse*` boundary | Only earns its keep once you're consuming a real backend's OpenAPI contract, not a public test API. |
|
||||
| Storybook + axe a11y gate | Testing/documentation infrastructure that pays off at a much bigger component count. |
|
||||
| CI pipeline | Nothing to gate yet with one context and no deploy target. |
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
## Growth path — when you outgrow this
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
Each of these is a real, working pattern in the POC — copy it when you actually need it,
|
||||
not before:
|
||||
|
||||
## Building
|
||||
- **A page's state grows past 2-3 interacting fields, or needs undo/multi-step flow** →
|
||||
add an Elm-style store: `shared/application/store.ts` (`createStore`) +
|
||||
a `*.machine.ts` per feature (Model/Msg/pure `reduce`).
|
||||
- **You have 2+ contexts that must not import each other** → add
|
||||
`dependency-cruiser` (`.dependency-cruiser.js`) to enforce the `domain → application →
|
||||
infrastructure`/`ui` direction this template already follows by convention but doesn't
|
||||
check.
|
||||
- **You're consuming a real backend's OpenAPI contract** → add a `contracts/` layer
|
||||
(wire DTOs) + a generated typed client + a hand-written `parse*` boundary in
|
||||
`infrastructure/` (see ADR-0001, `.claude/skills/bff-endpoint/SKILL.md` if you're
|
||||
working from the POC directly).
|
||||
- **A second locale** → wrap user-facing copy in `$localize` with a stable custom id and
|
||||
add a translation `.xlf` file (see the POC's `CLAUDE.md` "User-facing copy" convention).
|
||||
- **A real design system** → vendor your CSS, then bridge your own token names onto it the
|
||||
way ADR-0003 (`docs/reference/architecture/0003-cibg-huisstijl.md` in the POC) does —
|
||||
keep your token names stable, only their values change.
|
||||
- **Testing/a11y at real component count** → add Storybook + the axe a11y addon so every
|
||||
component's states are visually verifiable and accessibility-checked, not just
|
||||
behavior-tested.
|
||||
|
||||
To build the project run:
|
||||
## Folder convention
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
Each business capability is a context under `src/app/<context>/`, split into
|
||||
`domain/application/infrastructure/ui` — dependencies point inward
|
||||
(`ui → application → domain`; only `application` reaches `infrastructure`). Inside a
|
||||
context's `ui/`, components are organized by atomic-design layer (`atoms → molecules →
|
||||
organisms → templates → pages`); `shared/` holds only cross-context building blocks. When
|
||||
you add a second context, mirror `users/`'s shape.
|
||||
|
||||
Reference in New Issue
Block a user