Introduction
At the moment, validation has several problems:
- Validation is recursive and does never stop, loading all related Entities
- Unmodified Entities loaded from DB are validated
Additionally, most of the users of Flow do not design Aggregates in a manner that would satisfy the rules of Aggregate design in DDD. This makes it impossible to only validate Aggregates. Instead, validation should take Aggregate Boundaries into account and also be flexible enough to expand this boundary over more than one Aggregate.
Furthermore, validation should try to avoid loading lazy relations. This can be achieved by stopping at a given relation, if the related Entity is not already loaded and is considered an Aggregate Root by the system.
Finally, a way must be found to identify Entities that have been
a) loaded from persistence and
b) are unmodified
and skip validation there.
Validation Boundaries
Figure 1 visualizes the Validation Boundaries when taking Aggregate Boundaries into account. There are three cases displayed.
In case one, the first Aggregate holds a reference to another Aggregate. Given the relation is not yet loaded and is not annotated with @CascadeValidation
the validation would stop after validating the first Aggregate.
Case two describes what happens if an Entity is passed via action argument. In this case, validation can only validate the Entity out of context of any Aggregate. Any related Aggregates would be handled just like in case one.
In the final case, the relation to the second Aggregate has been annotated by @CascadeValidation
and thus the Validation Boundary covers both Aggregates. This will result in both Aggregates being validated at all times. If the second Aggregate is not yet loaded, it will be loaded, then validated.
Figure 1: 3 cases of Validation Boundaries
Implementation
The validation tree itself is built for classes, not instances. This makes it impossible to fully build the validation tree before the actual validation subject has been given. To still being able to recognise Validation Boundaries, I suggest adding a new validator, the AggregateBoundaryValidator
, that is automatically inserted at each relation to other Aggregate Roots. Just like the CollectionValidator
, it would then either build the remaining validation tree if validation is required (e. g. the Aggregate is loaded), or behave like the RawValidator
(no-op).
The validation tree that is initially created for a given class would then be created according to the Aggregate Boundary (see Figure 1, case 1).
Missing
- A concept for detecting unchanged Entities that have been loaded from persistence is still missing. Alexander Berl might have an idea here.