Introduction
After my lecture on Vaughn Vernon's "Implementing Domain-Driven Design.", I've decided to write a post which will be connected with DDD and draw attention to the underestimated Value Objects. In my experience, developers are abusing Entities in their projects. Very often that behavior ends with an occurrence of many anemic domain models without or with little business logic. In my opinion, some of the anemic domain models could be quickly transformed into the Value Objects.
Note: All examples are written in C#, and they are using newest C# syntax. I hope they would be easy to understand also for developers who are using different languages.
Example:
Let's start with the simple example of a Car entity like in the code snippet below.
public class Car
{
/* Properties */
public string Color { get; set; }
public bool Metallic { get; set; }
public string FuelType { get; set; }
public int EngineCapacity { get; set; }
{ ... }
/* Methods */
public void Drive(int distance) { ... }
{ ... }
}
We will focus on properties related to car's color. I mean Color and Metallic property. For simplification, color is represented by string type.
Characteristic of the Value Object:
1) Measures, quantifies or describes the subject of a domain
In our example, Color
and Metallic
are the best candidates for the Value object. Both are describing the look of the vehicle. So extract the new class:
public class Color
{
public string Hue { get; set; }
public bool Metallic { get; set; }
}
Whenever Hue
or Metallic
changes we receive a new color, so let's go to the next point.
2) Is immutable
Immutability means that object state cannot be changed after it's initialization. In Java or C# this could be achieved by passing all parameters to the constructor of a value object. Based on the values of parameters state of the object will be set up. Of course based on values some additional properties could be calculated.
Notice: The object itself can't call property setters either by the public, nor the private methods. This is forbidden.
Let's enhance our example according to previous thoughts.
public class Color
{
public Color(string hue, bool metallic)
{
Hue = hue;
Metallic = metallic;
if (hue == "white" && !metallic)
{
BasicPallete = true;
}
}
public string Hue { get; }
public bool Metallic { get; }
public bool BasicPallete { get; }
}
In the example above we've calculated the BasicPallete
property. For example, it could influence a car's price calculation.
3) Composes linked attribute values into the integral unit
This means that all properties are bounded, and each provides the crucial part of the information which describes the whole value of the object. Individual property value doesn't provide comprehensive information about the object. In our case knowledge about that if a color is metallic or not doesn't give us information about its hue. The same situation is when we know hue, but we don't know if it's metallic or not.
Worth is a perfect example cited often by this point. Let's take 100 USD as an example. Currency and amount are linked together. Let's imagine that someone has changed the currency from USD to MXN (Mexican Peso) in our object. That change has the significant impact. 100 MXN is worth approximately 5.25 USD. Oneone wants to be a victim of such a small change.
4) Is replaceable in the situation when the measurement changes
In our case car is an entity and color is a value object in our domain. Someday we decide to change the color of our car. Let's say we've bought a green non-metallic vehicle, but after a few years, we want to have a green metallic one. We cannot just add metallic gloss to existing painting. We need to repaint it with the new green metallic paint.
Color color = new Color("green", false);
//color.Metallic = true; -> This is not allowed
color = new Color("green", true);
5) Could be compared with other value objects
In languages like C# when we try to compare two objects by default, the comparison covers the location in memory of two objects (called Reference Equality). Two objects can have the same property values, but the will not be equal. This is the wrong assumption regarding the Value Objects. In that case, we need to have equality of given type and all property values (called Value Equality).
According to our example, two colors are the same if they have the same hue and are metallic or not. Two cars which are green and metallic have the same color.
So let enhance our class and implement the Value Equality:
public class Color
{
public Color(string hue, bool metallic)
{
Hue = hue;
Metallic = metallic;
if (hue == "white" && !metallic)
{
BasicPallete = true;
}
}
public string Hue { get; }
public bool Metallic { get; }
public bool BasicPallete { get; }
public override bool Equals(object obj)
{
return this.Equals(obj as Color);
}
public bool Equals(Color otherColor)
{
if (Object.ReferenceEquals(otherColor, null)) return false;
if (Object.ReferenceEquals(this, otherColor)) return true;
if (this.GetType() != otherColor.GetType()) return false;
return (Hue == otherColor.Hue) && (Metallic == otherColor.Metallic);
}
public static bool operator ==(Color leftColor, Color rightColor)
{
if (Object.ReferenceEquals(leftColor, null))
{
// null == null = true
return (Object.ReferenceEquals(rightColor, null));
}
return leftColor.Equals(rightColor);
}
public static bool operator !=(Color leftColor, Color rightColor)
{
return !(leftColor == rightColor);
}
public override int GetHashCode()
{
return (Hue.GetHashCode() * 0x100000) + (Metallic.GetHashCode() * 0x1000) + BasicPallete.GetHashCode();
}
}
6) Has side-effect-free behavior
This is the fundamental rule. Without it, Value object could be treated as a simple container for attributes. To understand it we should start with understanding side-effect-free function. Function without site effect has a result but doesn't modify its state. Such a function are fundamental in Functional Programming Paradigm.
All methods exposed by a value object should be a function without side effect, which doesn't violate the value object's immutability.
Let's take the car's repainting example. We can achieve repainting by adding such a function to our class.
public class Color
{
public Color(string hue, bool metallic)
{
Hue = hue;
Metallic = metallic;
if (hue == "white" && !metallic)
{
BasicPallete = true;
}
}
public string Hue { get; }
public bool Metallic { get; }
public bool BasicPallete { get; }
public Color Repaint(string hue, bool metallic)
{
return new Color(hue, metallic);
}
// Value Equality part
{...}
}
As we can see, there is a similar effect like in car's repainting case. But the connection between method and value object is stronger and side-effect-free. We don't modify the state of the color object. Instead of that, we've created the new one with demanded values.
Color color = new Color("green", false);
color = color.Repaint("green", true);
Summary
Of course, I didn't cover all topics related to Value Objects, all the more DDD, but I hope this post will inspire you to use it more often in your projects.
P.S.
Please don't go from one extreme to the other.
Links:
- Color.cs class
- Color.cs readonly struct
- Implementing value objects from .NET Guide
- Evans Classification from MartinFowler.com
- Value Object from MartinFowler.com which contains examples written in Java
Top comments (8)
This is great and all but I would suggest that you make the
Color
class a value type. Specifically I would write it asreadonly struct Color : IEquatable<Color>
), this has a couple of advantages:public Color Repaint(string hue, bool metallic)
)Color
to represent some values) if no virtualization is required.Thanks for your comment. You are completely right. My old habits (and laziness) won.
I had problems with storing Value Objects with Entity Framework. Iโve checked that even EF Core 2.0 doesn't support storage of struct objects.
In non-db scenarios read-only struct makes the job right. We need to keep in mind that C# 7.2 is required to use it. I added the struct version to the links too.
Point for you for your mindfulness.
Cheers.
Yes, value-objects are a pretty valuable (pun intended) tool to have on your belt. Thank you for the post.
The post scriptum reminds me of back in the day, I worked with Microsofts CRM 4.0 and they had encapsulated everything in such value objects which I think is a good example of overdoing things.
Thanks for this post. I think it is important to talk about Domain Driven Design, to study and study again, to put it into practice because it is not simple, it is not something simple just because it tries to express needs in the most adherent way to reality through software, but, at the end, it's really worth it
Thanks for good words :)
As you mentioned, talking and practicing DDD is vital for software engineers. We should keep in mind that usually, only a little subset of our system requires DDD. I think no more than 10-15% of the whole system. There is no sense to apply DDD to places where only simple CRUD is needed. That ends with so-called "over-engineering". Of course, everyone makes mistakes during the learning process. In my opinion, Domain Driven Design is worth this.
"it depends" is always a good starting point, in my opinion. But I'm sure that, for modern and complex systems, using only CRUD operations could lead to excessive and sometimes unmanageable complexity. It's a trade off: "forcing to follow this path" or "over-engineering" to better understand the code and keep its complexity under control over time is always worth it, but sometimes in our working environments this point of view is not fully accepted or understood: timing and costs are the greatest constraints
I totally agree with you. I โm also a big fan of the โIt dependsโ clause. We should ask if we need something in our solution.
Thanks for the article!
This is the best:
"Please don't go from one extreme to the other."