Context
ValidatorBuilder currently has several distinct responsibilities mixed
into one surface:
- Composing a chain of validators (
with, __call, __callStatic,
getValidators).
- Running the chain against an input and returning a value (
validate,
isValid).
- Running the chain and throwing on failure (
assert, check).
Anything that depends on ValidatorBuilder today depends on the full
surface, even when it only needs one of these capabilities.
Proposal
Extract the consumer-facing capabilities behind narrower interfaces. The
class keeps implementing all of them — this is about the public contract,
not internal structure.
Naming candidates
| Interface |
Methods |
Behavior |
Validatable |
validate, isValid |
Non-throwing, returns a value |
Assertable |
assert |
Throws on failure, full evaluation |
Checkable |
check |
Throws on failure, short-circuit |
Composition (with, __call, getValidators) is intentionally left out —
it belongs to the implementation, not a shared contract.
evaluate and evaluateShortCircuit are already covered by the existing
Validator and ShortCircuitable interfaces.
Naming regret
While we're touching this area: after using the library more as a client,
I've come to think ValidatorBuilder was the wrong name. Validator
would have fit it better — what users actually hold and call is a
validator, not a builder. The existing Validator interface would then
have been better named Evaluable, since evaluate() is its only
contract.
Renaming isn't part of this RFC, but it's worth flagging as context for
how we pick names going forward.
Non-goals
- Renaming
ValidatorBuilder or Validator.
- Splitting the class into multiple classes.
- Breaking the fluent API.
Drafted with assistance from Claude; reviewed and edited by me before posting.
Context
ValidatorBuildercurrently has several distinct responsibilities mixedinto one surface:
with,__call,__callStatic,getValidators).validate,isValid).assert,check).Anything that depends on
ValidatorBuildertoday depends on the fullsurface, even when it only needs one of these capabilities.
Proposal
Extract the consumer-facing capabilities behind narrower interfaces. The
class keeps implementing all of them — this is about the public contract,
not internal structure.
Naming candidates
Validatablevalidate,isValidAssertableassertCheckablecheckComposition (
with,__call,getValidators) is intentionally left out —it belongs to the implementation, not a shared contract.
evaluateandevaluateShortCircuitare already covered by the existingValidatorandShortCircuitableinterfaces.Naming regret
While we're touching this area: after using the library more as a client,
I've come to think
ValidatorBuilderwas the wrong name.Validatorwould have fit it better — what users actually hold and call is a
validator, not a builder. The existing
Validatorinterface would thenhave been better named
Evaluable, sinceevaluate()is its onlycontract.
Renaming isn't part of this RFC, but it's worth flagging as context for
how we pick names going forward.
Non-goals
ValidatorBuilderorValidator.Drafted with assistance from Claude; reviewed and edited by me before posting.