# Adoption POC — consumer form + API (G1/G2)
**Version:** smart-value-objects **4.0.0**
**Date:** 2026-08-03
**Goal:** Prove a consumer app replaces primitives and shares the same validation rules in form and API.
## Scenario
A profile form collects `title`, `name`, and `email`. Rules must match on:
- Client submit handler
- Server POST handler
- UI `maxLength` bindings
## Before (primitive obsession)
```typescript
type ProfileDto = {
title: string;
name: string;
email: string;
};
// Front: maxLength={255} invented locally
// Back: if (!email.includes('@')) throw ...
// Rules drift between layers
```
## After (catalog + shared schema)
```typescript
import type { ValidateRecordSchema } from 'smart-value-objects';
import { EmailAddress, PersonName, Title } from 'smart-value-objects';
export const profileFormSchema: ValidateRecordSchema = {
title: Title.tryCreate,
name: PersonName.tryCreate,
email: EmailAddress.tryCreate,
};
```
Import **the same** `profileFormSchema` in both handlers — see `examples/consumer-form-api/`.
## G1 — Encapsulate primitives
| Field | Before | After |
|-------|--------|-------|
| title | `string` | validated via `Title.tryCreate` → domain type at boundary |
| name | `string` | `PersonName.tryCreate` |
| email | `string` | `EmailAddress.tryCreate` |
Persist JSON with `getValue()` when crossing the wire.
## G2 — Same rules front and back
```typescript
import { Title, PersonName, EmailAddress } from 'smart-value-objects';
// 120
// 80
// 254
```
Never copy limits from README — read `X.constraints`.
## G3 — Aggregated errors
Both handlers call `validateRecord(input, profileFormSchema)` and return **all** field errors:
```typescript
const validation = validateRecord(body, profileFormSchema);
if (!validation.isValid) {
return { status: 400, errors: validation.errors };
}
```
Run the proof in-repo:
```bash
npm test -- examples/consumer-form-api
```
## Agent session (Cursor skill)
1. Install: `npm install smart-value-objects@^4.0.0`
2. Copy skill: `cp -r node_modules/smart-value-objects/skills/smart-value-objects-consumer .cursor/skills/`
3. Agent loads skill when editing consumer DTOs/forms
4. Agent must **not** use local regex for catalog types or invent `maxLength`
**Anti-pattern blocked:** `email: string` with inline `/^...$/`
**Required pattern:** `EmailAddress.tryCreate(raw, 'email')` + `EmailAddress.constraints`
## Checklist (roadmap metric)
| Metric | Status |
|--------|--------|
| VOs Must shipped (Email, Title, PersonName, Uuid) | Done in 4.0.0 |
| Form + API same `validateRecord` | `examples/consumer-form-api/` |
| UI limits from `constraints` | Documented above |
| Skill in npm tarball | `skills/smart-value-objects-consumer/` |
| npm `latest` = 4.0.0 | Published |
## Next (P1 backlog)
- `@smart-value-objects/zod` adapter from `FieldConstraints`
- `toFormErrors()` helper for React/Vue
- Real app POC in a separate consumer repository