Leaking Value Objects from your Domain

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.

YouTube

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.

Value Objects

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.

Messaging

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.

Conversion

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.

Source Code

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.

Related Links

Follow @CodeOpinion on Twitter

Software Architeture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

ALWAYS Valid Domain Model

Always having your Domain Model in a valid state means it will be predictable. You’ll write less defensive code or conditional code because your domain objects will always be in a valid state. Using aggregates is a great way to encapsulate the state with behavior to keep the state valid. Using factories to create your aggregates is key to having a valid state from the very beginning. Here’s how you can create an always valid domain model.

YouTube

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.

Aggregate

First, let’s start with defining our Aggregate as it ultimately is what will keep a valid domain model.

The example I’m going to use in this post is of a Shipment. You could think of a Shipment in the sense of Food Delivery service where you’re ordering food from the restaurant and it gets delivered to your home.

The aggregate in this scenario consists of a Shipment and two Stops. There is a Pickup Stop, which is the restaurant where the shipment starts from, and a Delivery stop which is your home where the Shipment ends. The Aggregate Root is the Shipment.

Valid Domain Model

One important aspect of this is that each stop needs to follow a progression. The initial state of a Stop is “In Transit”, then goes to “Arrived” once the delivery driver arrives at the location of the stop (either the restaurant or your home) and then finally goes to the “Departed” state when the delivery driver leaves the stop.

Valid Domain Model

Another rule is that this progression must be done for the Pickup Stop first, in its entirety before the Delivery Stop can start its progression. This makes sense because you need to arrive at the restaurant to pick up the food, leave the restaurant, arrive at the house for delivery, then leave the house.

Invariants

Based on the simplistic example above, our invariants are:

  • Shipment must have at least 2 stops
  • The First stop must be a Pickup
  • The Last Stop must be a Delivery
  • Stops must progress in order

The first three invariants must be established upon trying to create the Aggregate. The final invariant is controlled within the aggregate. In order to always be in a valid state, we must only allow the creation of a valid Aggregate that satisfies the invariants defined above. And once we have our Aggregate, we must only allow valid state transitions.

Enforcing these invariants is what will keep a valid domain model.

Factory

In order to create our Aggregate in a valid state right from the get-go, we can use a Factory. In the example below, I have a private constructor but expose two different static factory methods that force us into a good state. These factories are enforcing the first three invariants.

The Arrive() method is enforcing the last invariant that we must progress our stops in the correct order. Finally, here are the Stops (Pickup, Delivery) that enforce they transition themselves in the correct order.

Draft Mode

Often a scenario is what I call a “Draft Mode” where the invariants aren’t applicable, yet. In other words, they want to create a model that has much looser constraints.

To illustrate this with my Shipment example, you may have multiple Orders to a single Restaurant that ultimately will be placed all on the same Shipment.

In this case, the Shipment still has all the invariants but what we likely want is to have our Shipment created from a Plan. The concept of a Plan is to associate multiple orders and then generate a Shipment from them. This means we’re creating an Aggregate from another Aggregate.

ALWAYS Valid Domain Model

Having your domain model always in a valid state, right from the beginning means you’ll have to write less defensive code because you absolutely know the data is in a valid state. In my example, there will always be at least 2 stops, the first will be a Pickup, the last will be a Delivery. All stops will go through a progression, in order.

The factory is what sets everything up in a valid state and the Aggregate keeps us in a valid state.

Source Code

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.

Related Posts

Follow @CodeOpinion on Twitter

Software Architeture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

STOP doing dogmatic Domain Driven Design

The mainstream thought on Domain Driven Design is about Entities, Value Objects, Aggregates, Repositories, Services, Factories… all kinds of technical patterns. Because of this, most don’t think they need Domain Driven Design because it’s complicated for their domain. Why would you need all that “stuff”? Well, maybe you don’t! In a large system, modeling your domain, defining boundaries, and how they relate is far more important than concerning yourself if you’re using the Repository pattern correctly.

YouTube

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.

Domain Driven Design

I often use terms that people associate with Domain Driven Design (DDD), however, I generally don’t call out Domain Driven Design explicitly in many of my videos or blog posts. You probably have noticed that I do talk a lot about boundaries. Boundaries are probably the most important aspect for me that came from Domain Driven Design. However, the vast majority of content or discussions you will find online about DDD revolve around tactical patterns: Entities, Value Objects, Repositories, Services, Aggregates, etc.

While the tactical patterns have value, they must come after understanding the concept and value of defining boundaries with a Bounded Context. While a Bounded Context does get a lot of attention from Domain Driven Design enthusiasts, it’s not what people are introduced to first in tutorials or “sample” applications.

Here are some of the comments and questions I see the most related to Domain Driven Design:

DDD is only powerful when your business logic is complex

While I agree complexity is a driving factor, I also think it’s helped me most when dealing with larger systems. Complexity from the Domain as well as the complexity that comes from a large system.

In DDD you’re supposed to use a repository pattern.

You need to use factories to create entities or value objects.

This is the wrong concern. Focusing on patterns rather than boundaries and modeling the actual domain.

You have no logic in your entities, so that’s an anemic domain model, which is an anti-pattern.

It’s only an anti-pattern if you think you have a domain model but you really have a data model with transaction scripts. There isn’t anything wrong with starting with that and moving to a richer domain model as your understanding of the problem evolves.

You can’t have dependencies in your Domain Model

People go to incredible lengths to avoid have dependencies in their domain model. While again it’s a good practice to avoid dependencies and worth striving for, sometimes you actually need a dependency. Using Double Dispatch isn’t bad because you’re passing a dependency to a method and someone told you all dependencies must be injected via the constructor.

Dogmatic Domain Driven Design

I think all the questions above are losing sight of the benefits of Domain Driven Design. I don’t think concerning yourself solely along with tactical patterns and if they are being applied correctly is applying DDD. I think that’s applying dogmatic DDD.

To me doing Domain Driven Design is about understanding your domain, the language, the context of different people within it, the problem, and the solution space and trying to model it. And you’re not going to get it right at first as it will take time to iteratively build upon the insights you gain.

There’s value in the tactical patterns like Aggregates (which are a consistency boundary), but that shouldn’t be the focus. Just because you have repositories, aggregates, entities, doesn’t mean you’re doing Domain Driven Design. You just have a bunch of patterns.

Language

I find language a great way to understand the boundaries and define a bounded context within a system. A great example of this was from Mel Conway on Twitter.

Depending on who you’re talking to in your Domain, they likely have a different context given the subdomain they are in or the role that they have.

Exactly as Mel is pointing out, context matters in how language and intent is used.

As an example, in a Transportation company that transports freight/goods, there are multiple subdomains. Recruitment is who is hiring drivers, making sure they have the proper license, compliances, etc. Operations are concerned about the actual shipments and the freight being picked up and delivered.

Both have the concept of a Vehicle. But both have very different concerns and views on what a Vehicle is. They share the same term “Vehicle” but based on their context have very different views of what matters to me.

STOP doing dogmatic Domain Driven Design

In recruitment, the concept of a Vehicle maybe is owned by a Driver (known as an Owner Operator). They might care about the Vehicle Safety requirements and other compliances. Operations care about the availability of the vehicle and if it can do a particular shipment at a given time. Very different concerns.

Discovery

Understanding a Domain, Subdomains, and developing a Bounded Context can be very challenging. I like to use the analogy of walking into a dark room with only a small flashlight

At first, you have no mental model of what the room actually looks like. As you slowly flash the light around the room, you get a better mental model of how high the ceilings are, what’s the shape of the room, what’s on the floor. Your understanding grows slowly as you shine the light.

Understanding the boundaries of a domain takes effort and is the key to being able to understand the problem and solution space. This is the key to domain modeling.

Domain Driven Design

The title says it all. It’s not pattern-driven design, it’s Domain Driven Design. Don’t get caught up in the dogmatic majority that is focused on the tactical patterns. Again, yes they are valuable. Aggregate Design is a great way to define consistency boundaries. I’m not discounting the patterns, but that’s not the focus. The patterns are a means to an end.

Source Code

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.

Related Posts

Follow @CodeOpinion on Twitter

Software Architeture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design