---
title: Validation Strategy
type: concept
created: 2026-09-06
updated: 2026-09-06
sources: [codebase snapshot 2026-09-06]
tags: [validation, form-requests, domain-invariants]
---

# Validation Strategy

Validation is **split into two layers**: HTTP input validation at the boundary (Laravel FormRequests) and business-rule / invariant validation inside the Domain. This keeps framework validation out of the domain while still guarding core rules.

> **Note on provenance:** reflects the code as of 2026-09-06; no written ADR exists.

## Layer 1 — Input validation (Application/HTTP boundary)

`app/Http/Requests/` holds FormRequest classes per context and presentation style:

- `app/Http/Requests/Api/V1/User/CreateSessionRequest.php`

`CreateSessionRequest` validates `email` (`required`, `string`, `exists:users`) and `password` (`required`, `string`).

## Layer 2 — Domain invariants (inside aggregates/VOs)

The Domain enforces rules that are true regardless of input source:

- Each `*Id::fromString()` throws `\InvalidArgumentException` on an empty id.
- Aggregate factories enforce their own invariants (e.g. the removed `Order::place()` rejected a non-positive total).

These invariants live in the aggregate factory / value object constructor, so they cannot be bypassed by any caller.

## Observations / open questions

- **Only one FormRequest remains** (the login request). The web/API duplication that existed with the removed `Order` requests is gone.
- **No cross-field or authorization logic yet** beyond `authorize(): true` stubs.
- The `User` login path has a further validation/security question: it validates `email` against the DB in the FormRequest, but the actual credential check happens in `CreateSession` via `Hash::check` (see [user](../entities/user.md)).
- Client-side, the frontend uses **Zod** for input-boundary validation (e.g. `userFilterSchema`) — see [inertia-react-integration](inertia-react-integration.md).

## Related pages

- [ddd-layering](ddd-layering.md) — which layer owns which validation
- [cqrs](cqrs.md) — commands receiving validated input
- [user](../entities/user.md) — the leaky login path