A validation rule whose logic is an arbitrary predicate has no counterpart in the language, so it is left out of the document entirely — the reader is not told that a rule exists at all, only that something was dropped.
In FluentValidation this is Must / MustAsync:
RuleFor(_ => _.OrgNumber)
.Must(BeAValidOrganizationNumber)
.WithMessage(_ => CustomersMessages.Approve_OrgNumberRequired);
The declarative rules — NotEmpty, MaximumLength, GreaterThan, Matches, Equal, EmailAddress and friends — all map cleanly. Must does not, because its logic is a method.
Why it is worth a construct
Measured while generating from a real production application: ~248 Must/MustAsync rules, plus ~37 more with other non-declarative shapes (NotEqual, InclusiveBetween, IsInEnum). Together they are the single largest remaining category of dropped content — around 285 rules, comfortably more than everything else the generator cannot express put together.
The consequence is not just missing detail. A command with three declarative rules and four Must rules currently reads as a command with three rules. The document understates how constrained the input is, and a reader has no way to tell the difference between "this field has no further rules" and "this field has rules that could not be written down".
Recovering the message helps a little — Cratis/Arc#2409 now writes $strings. keys — but a message with no rule to attach to still has nowhere to go.
Suggested direction
A way to state that a named rule applies without expressing its logic. Something like:
validate
orgNumber
not empty
rule BeAValidOrganizationNumber message $strings.CustomersMessages.Approve_OrgNumberRequired
The name is statically available — it is the method or lambda the predicate resolves to — and it is meaningful to a reader in a way "a rule was dropped" is not. The document would then be honest about how many constraints a field carries, even where it cannot say what each one computes.
Points worth deciding:
- Whether an unnamed predicate (an inline lambda with no method to name) should be expressible at all, or should stay a diagnostic.
- Whether such a rule participates in anything the language does with rules elsewhere, or is purely descriptive.
- Whether the async variant needs distinguishing.
Related
A validation rule whose logic is an arbitrary predicate has no counterpart in the language, so it is left out of the document entirely — the reader is not told that a rule exists at all, only that something was dropped.
In FluentValidation this is
Must/MustAsync:The declarative rules —
NotEmpty,MaximumLength,GreaterThan,Matches,Equal,EmailAddressand friends — all map cleanly.Mustdoes not, because its logic is a method.Why it is worth a construct
Measured while generating from a real production application: ~248
Must/MustAsyncrules, plus ~37 more with other non-declarative shapes (NotEqual,InclusiveBetween,IsInEnum). Together they are the single largest remaining category of dropped content — around 285 rules, comfortably more than everything else the generator cannot express put together.The consequence is not just missing detail. A command with three declarative rules and four
Mustrules currently reads as a command with three rules. The document understates how constrained the input is, and a reader has no way to tell the difference between "this field has no further rules" and "this field has rules that could not be written down".Recovering the message helps a little — Cratis/Arc#2409 now writes
$strings.keys — but a message with no rule to attach to still has nowhere to go.Suggested direction
A way to state that a named rule applies without expressing its logic. Something like:
The name is statically available — it is the method or lambda the predicate resolves to — and it is meaningful to a reader in a way "a rule was dropped" is not. The document would then be honest about how many constraints a field carries, even where it cannot say what each one computes.
Points worth deciding:
Related