# Zod coexistence **smart-value-objects** and **Zod** solve different layers: | Layer | Tool | Responsibility | |-------|------|----------------| | Wire / DTO | Zod (optional) | Parse unknown JSON, optional fields, nested DTOs | | Domain | smart-value-objects | Typed value objects with invariant rules | ## Recommended split ``` HTTP JSON → Zod (optional shape) → tryCreate / validateRecord → domain VOs ``` Zod confirms `{ email: string, title: string }` exists. `EmailAddress.tryCreate` confirms the email is valid per catalog rules. ## Do not duplicate rules ```typescript // ❌ Bad — regex duplicated, will drift from catalog const schema = z.string().regex(/^[^\s@]+@...$/); // ✅ Good — domain validation after Zod shape check const parsed = z.object({ email: z.string() }).parse(body); const email = EmailAddress.create(parsed.email); ``` ## Future: `@smart-value-objects/zod` (P1) Planned helper to generate Zod string schemas from `FieldConstraints` so wire and domain stay aligned automatically. ## When to use only smart-value-objects - Internal services with typed handlers - Forms validated on submit with `validateRecord` - Agents following the consumer skill (no local regex) Use Zod when you already standardize on it for OpenAPI/tRPC and need nested optional DTO parsing.