Primitives vs Value Objects - Units of Measurement
Working with different units of measurement can lead us to bugs and make our code harder to maintain. Could we solve this problem with Value Objects?
NASA lost $125 million because of a missing conversion from imperial to metric units. The navigation team (Jet Propulsion Laboratory) was using metric units, whilst the acceleration data team (Lockheed Martin Astronautics) was using the imperial units (see news article).
As I was researching the topic, it appeared that there was an agreement that the acceleration data team, whose system was using imperial units would need to do conversion to metric units to satisfy the needs of the navigation team. But they didn’t do that - which led to NASA losing the Climate Orbiter.
What if NASA used DDD?
If NASA had used DDD, then there would not be any need to force any other teams to change their units of measure. Instead, the following would have happened:
The navigation team (Jet Propulsion Laboratory) would be using metric units as the canonical units of measurement for their system
The acceleration data team (Lockheed Martin Astronautics) would be using imperial units as the canonical units of measurement for their system
Thus, each team would use the units they typically use.
The knowledge of the difference would be resolved through the anti-corruption layer. More specifically, the Navigation team could have created an anti-corruption layer so that they convert data coming from external sources. For example, when getting data from the Acceleration data team, they would convert from imperial into metric units.
Height Classifier Example
I’ve taken the NASA story as a motivating example and built a simplified version of it to illustrate the problems of working with units of measurement.
We are building a Height Classifier application.
For some person’s SSN, we read the person’s height from an external system.
The external system provides us with height data in inches
We need to convert it to centimeters because that’s the canonical unit for our domain
Finally, we perform some business logic (for example, classifying the person as tall or not).
Let’s see the two approaches.
Using Primitives
When working with primitives, it means that in our domain, we have to keep track of height in both inches and centimeters. When we get data from the external system in inches, we need to convert it into centimeters before we work with it.
The problem with this approach is that it makes our domain code harder to maintain - we have to manage multiple units of measurement and conversions. We could accidentally cause bugs if we mix up the units or don’t do the conversions.
Using Value Objects
When we introduce the value object (Height), with its canonical unit of measure (centimeters), then our domain is more “pure“. It means in our domain, we don’t have to worry about the external systems or any conversions between our canonical unit of measure (centimeters) and the external unit of measure (inches).
This makes our domain model easier to maintain and helps reduce accidental bugs.
Code Demo (YouTube)
The source code is on GitHub: https://github.com/valentinajemuovic/sandbox-java
I don't quite get how you want to prevent "accidental bugs" by just introducing value objects. Another module/API or another developer could still pass the wrong primitive values (from a different unit of measure) to create the object. The naming is better and more obvious of course.