For those familiar with Domain Driven Design (DDD), this may seem like common sense. However, as more people adopt DDD concepts, this subtlety is definitely something to pay attention to.
Quick Overview of Entities
In DDD, an entity is a design pattern used to describe an object in the domain model that is truly unique; it has its own identity and distinctive behavior. An easy way to identify entities is to ask yourself, “If I duplicate an instance of this object, will it cause data integrity issues or other problems within my domain?”
For example, an address might be a value object or an entity, depending upon how it is used in your domain. If an address can be duplicated and substituted in places where an address is needed without a problem, then it is a value object.
However, if an address is truly unique, as it might be in a post office application, a duplicate address would cause data integrity problems. If a package needed to be dispatched to a specific address, and two of those addresses existed with the same identity, this condition would cause problems; a package can only be delivered to one address.
An Example of Incorrectly Implementing Entities
Entities should be expressed with as much simplicity as possible. An entity should only contain business logic specific to itself. This concept is defined as plain-old CLR objects in C#/VB.NET, etc. (POCO), POJO in Java, PORO in Ruby, etc.
If you find your entities are dependent on other objects, this is usually a design smell calling for the use of the service DDD pattern. Let’s use the canonical customer/order example:
Look at the following sample dialog with a domain expert. This person is an expert in some sort of online retailing business you might be contracted to write software for:
You: So tell me a little bit more about your business, who are your customers?
Expert: We sell groceries online to people living in the New York City area. We are fairly new in the market and are are looking to automate our business a little more, that’s why we’re hiring you!
You: Great! I’ll do my best to improve your automation. Let’s start with your customers, how do they place orders?
Expert: First, they browse around our online store, adding items to their shopping cart. Once they are ready to checkout, they hit the checkout button. We get an email from the customer containing a list of items from our inventory, and the quantity of those items that they want to purchase.
Your brain starts to flash with ideas on how to improve their current system. First, you might make a value object to encapsulate line items in an order as follows:
Next, let’s focus on the representation of the customer in the domain model. The customer is definitely an entity; it cannot be duplicated and substituted for another customer, because this would result in the order being processed for the wrong person.
A representation of a customer in a domain model needs an address, and the ability to change that address. The domain expert stated that they do not service areas outside of New York City. One way of expressing that in the entity is to throw an exception whenever the customer tries to change their address to a city other than New York City.
Recall the domain expert stated, “We get an email from the customer containing a list of items from our inventory, and the quantity of those items that they want to purchase.” This statement might lead you to think that you need to encapsulate this behavior on the Customer class itself.
What the expert was really trying to communicate was that their current system compiles the customer information along with the order information and packages it up into an email. Regardless, you go along with it, and start thinking about how to implement that behavior within the customer class.
To handle the orders, you might have an order generator that serves as a DDD factory to create an order, along with an order processor that actually places the order in the system.
Enough analysis, we now have a pretty good idea of what the first iteration of the Customer class should look like:
With this implementation, it seems very easy to place an order with code like this:
The Correct Way of Implementing Entities
The problem with the Customer implementation is that it is coupled to an order generator and an order processor. This implies that in the real domain, customers have knowledge of these two services.
The way a customer is expressed in this domain model is fundamentally flawed. Customers do not know of such things; I frequently shop on Amazon.com, and I have no knowledge of an order generator, nor a processor.
In DDD, the order generator is an example of the factory pattern. Factories are only used to support a domain model, not to define the domain model. There are exceptions where the domain actually has some concept of creating something new, such as real factories.
The order processor is an example of the DDD service pattern, encapsulating interactions between other entities and value objects. Other services or a higher layer should be consuming the order processor, not entities or value objects.
A more appropriate implementation of the Customer class can be defined by stripping dependencies on the two services from the class, distilling what is truly important to a customer in this domain model. In this example, all that would remain is the validation that occurs when setting a new address.
This greatly simplifies the Customer class, and allows the definition to be a POCO:
Notice how simpler the class is? It contains business logic only. The messiness of invoking the services has been removed. The removed behavior is an excellent candidate for another service, due to its interaction with entities and value objects. Let’s create an OrderService class:
The OrderService class adheres to the dependency inversion principle and can be configured with an IoC container to inject default implementations of the constructor dependencies. This service allows for interaction of the customer entity, line item value objects, an order generation service, and an order processing service.
It is quite common to have services be dependent upon other services. Services, factories, and repositories can have dependencies on other other pieces in the domain model or on infrastructure level objects.
We can now write a line of code to place an order similar to the previous line, without making direct calls on the Customer instance:
You should be weary of a design smell whenever your entities depend on other objects. These dependencies are typically domain model services or infrastructure pieces, such as logging or database support classes (ActiveRecord anyone?).
Keep your entities as simple and pure as possible. Leaving out threading, logging, and all of the other impurities that can muck up the class. Use services to encapsulate interaction between entities and value objects.
For further information, I recommend reading Eric Evans’ Domain Driven Design.
|Domain-Driven Design: Tackling Complexity in the Heart of Software|