How do you validate input when you have different cases?

Thanks for bringing up this discussion! I think a framework like Flow lives by challenging it’s concepts and rethinking things every now and then.

And oh boy, I do have a record track with Flow and validation and I do think there the framework is indeed missing something. And I do think, what you are feeling here is related to what I also discovered some years ago.

Quoting myself from RFC: Optimized Validation of Entities - #10 by aberl from way back in 2016 (yes, that long):

So the actual issue we face is, that Flow currently does not separate user input validation and enforcing business rules . The only thing that comes close is writing individual model validators, but it’s not easy to distinguish the two types of validators in order to skip the one type depending on context.

From a pure DDD viewpoint, the latter should actually happen inside the aggregates, ie. the aggregate is responsible for asserting it’s invariants. Now Flow currently doesn’t provide any nice flow (no pun intended) or best practice for doing such validations and notifying the user on the outcome, e.g. return validation messages (It could maybe be solved with Views.yaml configuration for a specific DomainInvariantException class).

From my viewpoint today, I would maybe say that Flow does not have any solution for “business rule enforcing” at all.

So with that being said, I would highly suggest to avoid doing “string”,“float”, etc. validations on any entity property alltogether (under some circumstances, property validators can act as a safe-guard to prevent running into wrongly persisted data, e.g. string length limits on a column). The only valid validations you should have on a domain model are business rules, which for Flow as mentioned probably translate best as custom Model validators. My gripe with that approach is the detachedness of the two concerns then. You have business rules in a class (Acme\Foo\Domain\Validator\MyDomainModelValidator) that is not physically close to the actual domain model it belongs to. This goes against a very valid heuristic to “keep things close together that change together”.

Hence, optimally, I’d say do 1) with a bit of encoding business rules into the domain model itself.
But of course in reality a lot of projects just deal with simple CRUD operations and in those cases, go for the easy way that Flows Validation framework handles well: annotate the entity directly, i.e. 2)
You will notice that this would be the right thing, when you start to just duplicate your entities into DTOs with added validation annotations and there is no logic involved between the input and what you persist to the database.

I haven’t done 3) really yet - I guess it could work well for cases where the endpoint decides the validation that needs to happen. Though maybe there it’d also be better to have that switch encoded in the domain model itself, because conditional validation sounds like a business rule.