One-time prettier --write so the new format:check CI gate starts green. .prettierignore excludes generated (api-client.ts, documentation.json), vendored (public/cibg-huisstijl), and backend (dotnet format owns it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
1.9 KiB
Markdown
57 lines
1.9 KiB
Markdown
---
|
|
name: value-object
|
|
description: Add a validated input type (postcode, email, hours, id number, …) as a branded value object with a parser — "parse, don't validate". Use whenever a form field or API value has format rules.
|
|
---
|
|
|
|
# Value object (parse, don't validate)
|
|
|
|
Raw input becomes a branded type only via a parser returning `Result`. Once you hold
|
|
the type, never re-check it. Never model validity as a boolean flag next to a string.
|
|
|
|
## Skeleton
|
|
|
|
`<context>/domain/value-objects/<name>.ts` (pure TS, no Angular):
|
|
|
|
```ts
|
|
import { Brand, Result, ok, err } from '@shared/kernel/fp';
|
|
|
|
export type Postcode = Brand<string, 'Postcode'>;
|
|
|
|
export function parsePostcode(raw: string): Result<string, Postcode> {
|
|
const t = raw.trim().toUpperCase();
|
|
if (!/^[1-9]\d{3}\s?[A-Z]{2}$/.test(t)) {
|
|
return err($localize`:@@validation.postcode:Voer een geldige postcode in, bijv. 1234 AB.`);
|
|
}
|
|
// The parser also normalises — callers always hold the canonical form.
|
|
return ok(t.replace(/^(\d{4})\s?([A-Z]{2})$/, '$1 $2') as Postcode);
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- The `as Brand` cast appears **only** inside the parser — the type is mintable nowhere else.
|
|
- Error message is user-facing → `$localize` with a stable `@@validation.<name>` id.
|
|
- Trim/normalise before testing; return the cleaned value.
|
|
- This is **format** feedback only. The server re-validates as authority (ADR-0001) — never encode business rules (existence, eligibility) here.
|
|
|
|
## Spec (required)
|
|
|
|
Co-located `<name>.spec.ts`: happy path, normalisation, each rejection case. Call the
|
|
parser directly — no TestBed.
|
|
|
|
## Wiring into a form
|
|
|
|
The machine's `validate(draft)` calls the parsers and collects errors into
|
|
`StepErrors`; the `Valid` type holds the branded values (see **form-machine** skill).
|
|
|
|
## Worked examples
|
|
|
|
`src/app/registratie/domain/value-objects/` — `postcode.ts`, `email.ts`, `uren.ts`,
|
|
`big-nummer.ts`, each with a co-located spec.
|
|
|
|
## Verify
|
|
|
|
```bash
|
|
npm test && npm run lint
|
|
```
|