Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Value Objects are a great way to explicitly capture concepts within your domain. They are immutable, always in a valid state, provide behavior, and are defined by their value. This sounds a lot like Messages (Commands, Events) that are also immutable and should be in a valid state. However, exposing your Value Objects by using them within Commands or Events can have a negative impact on your ability to evolve your domain model.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
First, let’s cover what Value objects are since they have many characteristics that define them. They are explicit domain concepts that should always be in a valid state. They are immutable once created, which means they are always created in a valid state. Since they cannot be mutated they also have the benefit of being defined by their value. Lastly, they should have behavior, which is a characteristic that is often overlooked.
A typical example of a Value Object is Money. Money isn’t just a decimal. Especially in a multi-currency environment. The combination of the amount and the currency is required to make it valid.
Another typical example is Distance. Again, the distance isn’t simply a number, but rather can be a number along with a unit of measure.
Commands and Events in a Message or Event Driven Architecture look very similar to Value Objects. Messages are explicit domain concepts that are immutable and in a valid state. So can you have Value Objects inside a Command or Event?
In the example above, the PlaceOrderCommand has two Value Objects: Product and Currency. These are explicit concepts we’ve defined within a boundary.
Since messages are for the purpose of decoupling between boundaries, this means that other boundaries must be aware of these as concepts.
Putting Value Objects in your messages means you’re going to be leaking details outside of your service boundary. The consequences of this are that since messages are contracts, you’ll need to think about versioning any time you want to change a Value Object since it’s a part of a Message.
Rather, you want to keep domain concepts from leaking outside of your service boundary. Concepts within your service boundary you want to be able to refactor, change, rename, remove without having to concern yourself with other services. The purpose of messaging is decoupling and using messages as a stable contract. The moment you leak something like a Value Object into a message, you’ve coupled other services to concepts within your service boundary.
Instead of leaking Value Objects, you can create Messages/DTOs that may look similar. Simply have some type of conversion that accepts your Value Objects as parameters but have the message being built be simple primitives or nested types.
As another example, that’s using a nested type.
Don’t Leak Value Objects
They are as much a domain concept as Entities are. If you’re not going to expose Entities then do not expose Value Objects. Although they may seem trivial, you’ll be handcuffed into versioning your messages if you do need to change a them in any way.
Messages are contracts for other services. You want to message contracts to have some stability. Although Value Objects have similar characteristics as messages (Commands and Events), they are meant to be internal while Messages are meant for other services boundaries.
Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.
- Does CAP Theorem apply to Microservices?
- REST APIs for Microservices? Beware!
- Do Microservices require Containers/Docker/Kubernetes?