crossing-io-boundarieslisted
Install: claude install-skill yutna/functional-design-skills
# Crossing IO Boundaries
## Overview
Domain types are designed for rules; wire and storage types are designed
for transmission and querying. They are different jobs, and letting one
type do both means every schema change is a domain change and every
domain change is a migration.
The boundary is an explicit, deliberate mapping in one place: parse
inward into fully valid domain values, serialise outward into the shape
the outside expects. Everything between those two functions can then
assume its values are correct, and everything outside can evolve on its
own schedule.
## When to use
- Designing an API payload, message, or database schema
- A domain type has nullable fields that only storage needs
- Adding a field to a domain type requires a migration and a version bump
- Deciding where validation happens for incoming data
- An ORM entity is being used as a domain object
Not for: deciding what the domain types should be, which is
[modeling-with-algebraic-types](../modeling-with-algebraic-types/SKILL.md).
## Core rules
1. **Two type families, always.** Domain types inside; transfer types at
the edge. Even when they look identical today.
2. **Transfer types are dumb.** Primitives, plain records, nullable
fields, no invariants. Their job is to survive transmission.
3. **Parse inward, serialise outward.** One function each direction, per
boundary.
4. **Parsing returns `Result`.** The outside can always send something
the domain forbids.
5. **Serialising is tota