---
title: "0018. Share one validation seam across layers, in two phases"
description: "Accepted, 2026-10-20"
canonical_url: "https://docs.galleon.thec1oud.uk/docs/decisions/0018-shared-validation-seam"
markdown_url: "https://docs.galleon.thec1oud.uk/docs/decisions/0018-shared-validation-seam.md"
last_updated: "2018-10-20"
x_farming_labs_generated_preamble: true
---

# 0018. Share one validation seam across layers, in two phases
URL: /docs/decisions/0018-shared-validation-seam
LLM index: /llms.txt
Description: Accepted, 2026-10-20

# 0018. Share one validation seam across layers, in two phases

**Status:** Accepted · **Date:** 2026-10-20 · [Source on GitHub](https://github.com/thec1oud/galleon/blob/dev/docs/adr/0018-shared-validation-seam.md)

## Context

Validation isn't only an API concern. Domain code, ORM changesets and form
handling validate too, and some rules need services ("this email isn't
taken"). If each layer has its own result shape, errors can't be mapped
consistently to responses.

## Decision

- **One result shape**: a list of field errors, each with a path (JSON
  Pointer), a stable code, parameters and a message key for translation. It
  maps into the error model and problem+json automatically.
- **Two phases**: **shape** validation is pure and synchronous (types,
  ranges, formats, cross-field rules) and is read from struct tags at boot,
  or will be generated by Themis; **semantic** validation gets a `context`
  and can use a read transaction.
- Generated and hand-written rules are to compose in a defined order.

## Consequences

- Every layer reports validation failures the same way, and clients see one
  error format.
- Themis will be able to generate matching client-side validators.

## Alternatives considered

- **API-only validation.** Leaves domain and form validation to diverge.

## Sources

- [Part 4: Pushback 10, the validation seam](https://github.com/thec1oud/galleon/blob/0897d74/docs/design/04-desires-debate.md#pushback-10-themis-the-api-dsl)

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
