Given the following example:
class Person {
Integer age
String lastName
String firstName
}
The property age should be constrained to specific validation rules:
- Higher than 0
Same for the lastName and firstName:
- These strings should not contain special characters (e.g. numbers, underscore, ...)
- Length should be > 0
In order to abstract this validation policy, should I create value objects such as age and name in order to encapsulate the validation:
class Age {
Integer value
}
class Name {
String value
}
class Person {
Name lastName
Name firstName
Age age
}
Indeed I can also keep the code DRY and re-use my value objects but it seems like an "over-abstraction"
Sidebar: falsehoods programmers believe about names.
should I create value objects such as age and name in order to encapsulate the validation
It's a trade off: the creation of a value type allows you to limit the number of places that validation is required in your program.
With a strong type checker, using a specific type allows the compiler to protect the programmer from a class of errors.
Furthermore, the creation of value types gives you a natural home for methods related to the state.
It also insulates the consumers of Age from the in memory representation; for example, if you were to later decide that you wanted to change the units of age, or that age should track the specific time that was age=0, then you can make that change in one place, rather than everywhere. This is right from Parnas -- Age
serves as a boundary around the decision to use an integer representation in memory.
The question "where are we using age in our code" is a lot easier to answer when age is not merely a domain agnostic type.
Against that - it adds does add some complexity to introduce value types.
In many cases, the benefits of defining domain specific types outweigh the costs.