canonical input boundary
situation
an integration received structured identifiers and authored labels from several sources. early code trimmed segments, changed case, and derived keys from labels. it held until an identifier contained an unexpected delimiter and a label edit changed the resource it described.
the failure was not one wrong string method: it treated three kinds of input — structured syntax, closed external values, authored meaning — as interchangeable strings.
why
the three kinds carry different guarantees; convenient normalization destroys all three at once. once boundary code rewrites input, consumers cannot tell authored identity from derived display, and an editorial change silently becomes an identity migration. parse and validate before business logic reads input: every transformation auditable, invalid state unrepresentable past the boundary.
invariants
- structured value is parsed before business logic reads it.
- authored name/key stored exactly, with explicit ownership.
- closed external value validated and exhaustively mapped.
- malformed input → classified diagnostic, never a plausible rewrite.
decision process
| candidate | reasoning | decision |
|---|---|---|
| normalize all inputs into convenient strings | hides grammar, identity, and provenance differences; later callers cannot recover them. | rejected |
| derive a business key from display copy | turns editorial change into an unreviewed identity migration. | rejected |
| regex-rewrite a structured field before parsing | can alter valid syntax or accept malformed structure without a causal error. | rejected |
| parse/validate once, keep named fields, map closed values explicitly | makes invalid state unrepresentable after the boundary and keeps each transformation auditable. | selected |
resulting design
- boundary parser returns typed parts for structured input.
- explicit schema validates external payloads.
- named mapper converts validated provider members to internal members.
- authored key is an independent required field with a unique constraint.
- display formatting happens only at the rendered edge, never re-read as resource identity.
diagnosis is direct: parser failure asks for correction, unknown closed value asks for contract support, authored-key collision asks for a new explicit key. no repair path guesses what a rewrite meant.
verification
- test valid structured input with escaping/delimiters a string split would mishandle.
- send malformed syntax; confirm a stable correction diagnostic.
- send an unknown closed value; confirm the mapper fails visibly.
- edit display copy while retaining the authored key; resource identity must stay unchanged.
- search boundary and consumers for rewrite/casing APIs that could create a second canonicalization path.
good and bad
bad
return createWorkflow(requestBody as CreateWorkflowInput);
untrusted input crosses into the domain service through an assertion. malformed structure, unknown closed values, and rewritten identity all pass silently; first evidence is data whose provenance nobody can reconstruct.
good
return createWorkflow(CreateWorkflowInputSchema.parse(requestBody));
one canonical, diagnostic input boundary: invalid state unrepresentable past it, authored keys exact through display edits, each failure mode arrives with a named correction path instead of a plausible rewrite.