You should be using Value Objects 💊

How do you ensure that two given components speak the same language? Do you have invalid values floating around? Would you represent dates as strings internally? Then why do you represent your domain concepts as strings and integers? It makes no sense.

Luís Soares
May 13, 2020 · 6 min read
Image for post
Image for post

📝 Java enums are a native example of value objects but they’re not adequate for an arbitrary amount of values. Typical value objects are email, password, locale, telephone number, money, IP address, URL, entity identifier, rating, file path, point, color, date, date range, distance.

Value objects are one of the building blocks of domain-driven design. Let’s explore their main characteristics (using Kotlin).

Carry a value

The value object is a container and carrier of an arbitrary value:

The literal value is kept inside the value object 🚀

Usually, a value object holds a single value, but it can hold more. For example, an RGBA color holds 4 integers, a 3D point holds three numbers. This improves API semantics:

Conceptually speaking, value objects can be seen as literals like 42.0, 'A' or "John Wick". Value objects are the cure to the primitive obsession anti-pattern; they enclose their real type as an implementation detail; for example, if you’re changing the identifier of the User entity from integer to string, you wouldn’t have to change entities that use it or declarations like this:

findById(userId: UserIdentifier) // all the calls keep the same

Same value? Always equal

When you take a pill from a bottle, does it matter which one you pick? Two value objects carrying the same value(s) are always equal when compared, even if their references are different. Email("john@email.com") will always equal Email("john@email.com") regardless of their references:

Value objects with the same value are always equal 🚀

Objects that are equal due to the value of their properties […] are called value objects. Value Object, Martin Fowler

Immutable

Immutability is the most important characteristic of value objects. It's an essential property in functional programming. Value objects are native in languages like Clojure. In other languages, it pays to make the objects immutable, namely to prevent the aliasing bug. Once an Email is created, you can’t change its value:

Compilation error when reassigning the value of a value object 🚀

Immutable values can freely be passed around. There is no need to create copies. Even concurrent access is safe without modifications. Testing is simplified to creating different instances via the constructor and then asserting the returned results of properties and methods. Immutable objects also encourage side-effect free functions and those come with their own set of advantages. Value Objects, Florian Benz

No identity and no history

Value objects don’t make sense on their own. They exist in the context of an entity. You might create a new one to run a findByEmail for example, but other than that, they exist only to serve as entity data.

While entities have a long-lived identity granted by their properties, the value object’s identity is solely given by the piece of data they carry. Since it does not evolve, it means it does not have a life of its own.

Entities live in continuum, so to speak. They have a history (even if we don’t store it) of what happened to them and how they changed during their lifetime. Value objects, at the same time, have a zero lifespan. We create and destroy them with ease […] we don’t store value objects separately. The only way for us to persist a value object is to attach it to an entity. Entity vs Value Object, Vladimir Khorikov

Self-validated

How do you know that you have valid telephones floating around your application? With value objects, you can prevent invalid value objects from existing at all. This is the best way to have a centralized validation rather than validating all over the place (e.g. in multiple handlers). By the way, you should self-validate entities as well.

Value object with validation 🚀

By using and validating value objects, one does not only catch errors early on but also improves security. It’s also valuable information about the domain. Value Objects, Florian Benz

Can encapsulate logic

There’s nothing wrong with encapsulating a few bits of logic if they belong to the value object:

Adding logic to the email data class 🚀

Another example is when you want to create operators around value objects. Let’s define the plus of two points:

Kotlin operator overloading 🚀

Carry meaning

Value objects help APIs and entities reveal their intent, acting as code self-documentation. They’re like new types that enrich your type system by encapsulating their logic and creating terminology. Imagine the Customer entity; most of its properties can be value objects:

Customer entity — with and without value objects 🚀

Compare these signatures:

fun notifyClient(email: String)
🆚
fun notifyClient(recipient: Email)

The parameter of the first signature uses a variation of the Hungarian notation, which is a bad pattern in a modern language. The second signature focuses on the parameter meaning — its type is not needed.

Image for post
Image for post
IDEs can help with names and parameter types

Finally, compare these example invocations — the second doesn’t allow any misunderstanding about the order of arguments:

sendSms("test123", "text")
🆚
sendSms(UserId("test123"), "text")

It’s often a good idea to replace common primitives, such as strings, with appropriate value objects. While I can represent a telephone number as a string, turning into a telephone number object makes variables and parameters more explicit (with type checking when the language supports it), a natural focus for validation, and avoiding inapplicable behaviors (such as doing arithmetic on integer id numbers). Value Object, Martin Fowler

Value objects are domain-specific types, enforcing and promoting domain terminology.

They make the domain explicit, e.g. by using Money as a wrapper instead of just two fields of type BigDecimal and String […]. Readers of the code will understand it's about money and maintainers will be unable to pass something not representing money. In addition, the wrapper can contain validations and thus additional domain knowledge and also increase security. Value Objects, Florian Benz

Bonus: self-normalized

For flexibility reasons, and to avoid weird bugs, you might be more open regarding the input that you’re willing to receive. For example, you could automatically convert " John.Doe@Gmail.com " to "john.doe@gmail.com":

Implementation of a value object in Kotlin without using a data class 🚀

Normalization is part of parsing, which is an I/O responsibility. However, value objects belong to the domain in the clean architecture. This is why self-normalization is a bonus/optional/it-depends.

⚠️ You may decide not to self-normalize value objects but you need to make sure you self-validate them. Otherwise, you may end up with a database full of repeated emails but with different letter casings.

Conclusion

A value object is an envelope carrying simple immutable data. It’s solely identified by that data whereas an entity has a regular identifier with multiple pieces of data. Both can have associated logic.

A primitive obsession in your app is almost as bad as a user form with text fields for everything or a database full of anything but strings: you lose meaning and allow wrong input. Value objects bring semantics to your APIs and entities. In the context of an app, I see value objects at the same level as primitive types. In some cases, they’ll make the compiler help you to reduce mistakes.

Given the low effort involved and its great benefits, I recommend to recognize value objects and create them from early in the project. You’ll have a warning sign the moment you do any kind of logic with literals (e.g. parsing, splitting, validation, conversion, operations).

Value objects are valuable. Value objects make sense in most languages, object-oriented or not. Time to search for “value objects in [your language]”.

📝 For details about Kotlin value objects, check out Value Objects by Florian Benz. In Java, consider Lombok’s @Value annotation to create value objects.

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store