Jeffrey Palermo wrote an article about the fallacy of the always-valid entity recently in which he highlighted that guarding against an entity becoming invalid by having validation logic in the setters of properties raises more issues than it solves.
Another way of enforcing an always-valid entity is, what I have found elsewhere termed, the "verbose constructor". Having a constructor which requires a value for every property, or every valid combination of properties.
public Car(string make, string model, string serialNumber,
decimal engineLitres, int maxPower, decimal urbanMpg,
decimal extraUrbanMpg, decimal combinedMpg,
int fuelTankCapacity) { ... }
Car myCar = new Car("Audi", "A4", "12345ABCD", 1.8, 120,
28.5, 51.4, 39.8, 65);
The "verbose constructor" is itself a good candidate for an anti-pattern for the following reasons:
- Modifying the entity means modifying every instantiation of it also
- Forcing a programmer to set these values when they may not be available will result in garbage data which is useless anyway
- Derived classes must call this verbose constructor either replicating every parameter in their own constructors or hardcoding values for some parameters (another anti-pattern)
- Unreadable code, without help from the IDE it's difficult to identify each parameter
The method Jeffrey highlighted in his post has to use exceptions to catch validation errors, which forces a programmer to use another anti-pattern; exception handling. Where programming logic is implemented by the throwing and catching of exceptions. Any call to a property setter of the entity must be wrapped in a try...catch... block.
The always-valid entity, however it's implemented, seems to lead to a minefield of bad code. The thinking behind the need for this blurs the line between two important distinctions:
- not setting a value vs. setting it to something incorrect
- what makes an object a valid type vs. what makes an object valid for a particular use
I touched on the first of these before; where it may be the case that a programmer doesn't yet have a value for a particular property available yet. So long as we ensure the entity doesn't leak out in this invalid state we're okay.
Which leads on to the second item; we should guard against a value being set where that value violates the type e.g. setting Day to 32 for a DateTime, something bad has occurred and this is a real exception case. However, this is a much lower-level problem than enforcing business rules, for example where a credit card applicant's date of birth must be at least 18 years earlier than the date of the application. It could be the case that one company actually requires applicants to be 21 so hardcoding that type of validation into the class is not only restrictive but enforcing this by throwing exceptions is very inefficient.
The always-valid entity just serves to be very restrictive and limits the entity's uses in its current form. Validating from outside means that its easy to skip validation or make it more stringent when needed. Also, if implemented along the lines Jeffrey describes in his post, it means mechanisms have a common way of validating objects without needing specific knowledge of the particular object or the current validation being applied which makes for more robust code.