I'm working on a system that allows Administrators to define Forms that contain Fields. The defined Forms are then used to enter data to the system. Sometimes the Forms are filled by a human via a GUI, sometimes the Form is filled based on values reported by another system.
For each Field, the Administrator can define a Validation Rule that limits the allowed values for the Field. The Validation Rules can be anything from "the value entered in the Field must be True or False" to "the value entered in the Field must exist in column A of table B in the database". The Administrator may at any time change the Validation Rule for the Field.
In this scenario, what in your opinion is the most suitable place for validating that each Field is filled correctly? I currently have two main approaches in mind:
Option #1: Validate in the Domain Model
Each Field object would contain the Validation Rule specified by the Administrator. The Field objects would also have a reference to an IValidator. When an attempt is made to set the value of the Field, the Field would pass the given value and the Validation Rule to the IValidator. If the given value is not valid, a ValidationException would be thrown and appropriately handled in the GUI/interface to the other system.
- Strong protection against Fields being accidentally assigned values that violate the Validation Rule
The Data Access Layer needs to be able to bypass the validation and construct Fields that violate the current Validation Rule. Despite the Administrator changing the Validation Rule for a Field, we still need to be able to construct Field objects based on the old data e.g. when rendering a Form that was filled years ago. This could potentially be resolved by storing the current Validation Rule whenever we store the Field.
In this design, the Field model has an indirect link to the Data Access Layer/Repository via the IValidator. The injection of Services/Repositories to Domain Models seems to be generally frowned upon.
Option #2: Validate in a Service
Try to ensure that all attempts to set the value of a Field pass through a Service that ensures the Validation Rule holds. If the Validation Rule is violated, throw a ValidationException.
Of course, the Data Access Layer would not use the Service when creating Field objects that have previously been persisted in the DB.
Does not violate the "don't inject Services/Repositories into your Domain Models"-thinking.
No need to persist the current Validation Rule when persisting the Field. The Service can simply look up the current Validation Rule for the Field; when looking at history data, the value of the Field will not be changed.
- No guarantee that all logic that should use the Service to set the Field value actually does so. I see this as a major drawback; all it seems to take is someone writing "thisField.setValue(thatField.getValue())" and the Validation Rule of thisField might be violated without anyone being the wiser. This could potentially be mitigated by ensuring that the value of the Field matches with the Validation Rule when the Data Access Layer is about to persist the Field.
I currently prefer Option #1 over Option #2, mainly because I see this as business logic and feel that Option #2 poses a greater risk of introducing bad data to the system. Which option do you prefer, or is there another design that fits this scenario better than the two options described?
Edit (Complexity of validations)
The validation cases that have come up for now are relatively simple; the Field value must be e.g. numeric, a date, a date with a time or be an existing value in a database column. However, I suspect complexity to gradually increase over time. For example, the validation solution needs to be built with internationalization in mind - things such as Dates may be input in a locale-specific syntax.
I've decided to proceed with Option #1 for now, attempting to take care not to assign too many responsibilities to the Domain Model. Those facing a similar situation may also want to check out the related questions Validation and authorization in layered architecture and Data input validation - Where? How much?.