Enhancing descriptiveness with value objects
samson baraka
Lead Software Engineer | Backend Engineer | ERP & CRM Systems Engineer | Agile Practitioner | Microservices | AI Driven Systems | Java & Spring Developer | Enterprise Application Developer | Web developer
Implementing Domain-Driven Design pointed out quite well that we should use value objects to measure, quantify, or describe things from our problem domain. For example, you can describe an ID attribute with a value object instead of a long or integer value. You can wrap a double or big decimal attribute into a specific value object to express quantification more clearly.
We're not fully satisfied with just using the built-in language types to model a problem domain. To make the system more explicit about its nature and purposes, we wrap those built-in language data types—and even our own created types—into well-defined value objects.
This effort to convey meaning is based on the following two fundamental characteristics about value objects:
? They are immutable.
? They don't have an identity.
Suppose you have painted a picture. Imagine how strange would it be if, for some reason, after you've finished your work, parts of your picture mysteriously change colors. Colors in this example are like value objects that we use to create a picture, and each color can be a value object. So, to ensure our paint will persist, the colors, once used, must not change and must be immutable once used. I base my argument for value objects on this idea that some characteristics must never change because they are the raw material we use to describe a problem domain.
Raw material alone neither expresses much meaning nor has much value. The real value comes when we combine and work in that raw stuff to form relevant and discernable things. Because value objects alone are like raw material, we don't bother to replace them or throw them away. The bottom line is that value objects should be discardable and easily replaceable objects that we use to compose an entity or other type of object.
When modeling an entity class, for example, we have two options: to use or not use value objects on entity attributes. Here is an example of the second approach:
public class Event implements Comparable<Event> {
private EventId id;
private OffsetDateTime timestamp;
private String protocol;
private String activity;
...
}
Consider the following log excerpt as data entries we want to parse into the Event objects:
00:44:06.906367 100430035020260940012015 IPV6 casanova.58183 >
menuvivofibra.br.domain: 64865+ PTR? 1.0.0.224.in-addr.arpa.
(40)
00:44:06.912775 100430035020260940012016 IPV4 menuvivofibra.
br.domain > casanova.58183: 64865 1/0/0 PTR all-systems.mcast.
net. (75)
After being properly parsed, we would have Event objects with network traffic activity string fields, like:
领英推荐
casanova.58183 > menuvivofibra.br.domain
Before the greater-than sign, we have the source host and, after, the destination host. For the sake of this example, let's see it as an activity representing the source and destination of a packet. By being a string, it leaves a burden for clients that want to retrieve the source or destination host from the string, as illustrated here:
var srcHost = event.getActivity().split(">")[0]
//casanova.58183
Let's try with an Activity value object, as follows:
ublic class Activity {
private String description;
private String srcHost;
private String dstHost;
public Activity (String description, String srcHost, String dstHost){
this.description = description;
this.srcHost = description.split(">")[0];
this.srcHost = description.split(">")[1];
}
public String retrieveSrcHost(){
return this.srcHost;
}
}
Then, we update the Event entity class, as follows:
public class Event implements Comparable<Event> {
private EventId id;
private OffsetDateTime timestamp;
private String protocol;
private Activity activity;
...
}
The client code becomes clearer and more expressive, as we can see in the following snippet. Also, clients don't need to handle the data themselves to retrieve the source and destination hosts:
var srcHost = event.getActivity().retrieveSrcHost()
//casanova.58183
With value objects, we have more flexibility and control over our data, letting us express the domain model in a more cohesive way.
Next episode, we'll discuss assuring consistency with aggregates.