Files
atomic-design-poc/.claude/skills/value-object/SKILL.md
Edwin van den Houdt e82309786d style: format frontend, docs and skills with prettier; add .prettierignore
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>
2026-07-03 13:39:31 +02:00

1.9 KiB

name, description
name description
value-object 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):

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

npm test && npm run lint