good-value-object
good-value-object copied to clipboard
Validation
First of all - great work putting this document together.
In https://github.com/zverok/good-value-object#construction you describe #initialize
's role as "just validate and store in instance variables". What's the expected behavior on a validation failure?
Do you see any reason for a Value Object to exist with invalid input data (with degraded functionality) or existence of such objects should be prohibited - perhaps by throwing an Exception?
This has some implications. For example - what in case when calling an operator on two objects results in the creation of an invalid object?
Eg. TimePeriod.new(2, 'seconds') - TimePeriod.new(3, 'seconds') # Time is monotonic ;)
That's an interesting question, but probably outside of the scope of generic "value object" recommendations: that's something only domain might decide.
In some domains, value objects should at least validate types (e.g. TimePeriod.new(nil, 'seconds')
should raise), but in others duck-conversion is OK, and in even others value objects are used for internal calculations where every millisecond is important, and data is already known to be sanitized (so, even basic is_a?
might be unwanted).
Sometimes it makes sense to do an excessive validation of everything (say, TimePeriod.new(10_000_000, 'seconds')
might be considered out of (some) range of acceptable values).
Sometimes you just want to quick play-and-experiment and have the minimal (but still useful) object with very visible structure.
The same is with operators: it is for the domain to know. In Ruby's core, say, 1 / 0
is ZeroDivisionError
, but 1.0 / 0.0
is just Float::INFINITY
.
So, the generic and most-of-the-time useful answer (which I hope most of the recommendations in the guide are) is: "Decide yourself" :)