A rule about which orders are eligible for a discount, expressed three times: in a query, in a validation check and in a report.
final class EligibleForDiscount
{
public function isSatisfiedBy(Order $order): bool { /* ... */ }
public function toQuery(Builder $q): Builder
{
return $q->where('total_cents', '>=', 5000)
->whereNull('discount_id')
->where('created_at', '>', now()->subYear());
}
}
The two methods must agree and nothing enforces that, which is the pattern’s real weakness — a property test generating orders, checking both paths and asserting the same answer is the only thing that keeps it honest. It pays when the rule is genuinely used in both an in-memory check and a query; when it is used in one, it is a method with a ceremony around it.