validation
Request validation
Two kinds of validation, one boundary
Syntactic validation answers a question about the request alone: is the title present, is that a well-formed email, is the quantity positive, is
publishedAt before expiresAt. Everything needed is in the payload. This is what RequestValidation is for, and it is declarative.
Business validation answers a question about the world: is this ISBN already taken, may this account place an order, is there stock. It needs a repository, a clock, or another service.
The boundary is one question: does answering it require I/O? If yes, it is not request validation — it belongs in the use case or the entity, in
-core, and it fails by throwing a domain exception that the ktor-toolkit:problem-details skill maps. Putting an ISBN-uniqueness check in
rulesFor would drag a repository into the plugin configuration and make the rule untestable without a database.
Cross-field rules are still syntactic as long as both fields are in the request. invariant exists for exactly those.
Before writing rules, ask
The toolkit cannot guess business constraints, and a wrong guess ships as a 400 for a legitimate request. Ask the user directly: