Entities Should Not Depend on Other Objects

iStock_000004447463XSmall 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:

   1: class LineItem
   2: {
   3:     public LineItem(uint itemId, uint quantity)
   4:     {
   5:         ItemId = itemId;
   6:         Quantity = quantity;
   7:     }
   8:  
   9:     public uint ItemId
  10:     {
  11:         get;
  12:         private set;
  13:     }
  14:  
  15:     public uint Quantity
  16:     {
  17:         get;
  18:         private set;
  19:     }
  20: }

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:

   1: class Customer
   2: {
   3:     private readonly IOrderGenerator orderGenerator;
   4:     private readonly IOrderProcessor orderProcessor;
   5:     private Address address;
   6:  
   7:     public Customer(IOrderGenerator orderGenerator, IOrderProcessor orderProcessor) : this(orderGenerator, orderProcessor, new Address())
   8:     {
   9:     }
  10:  
  11:     public Customer(IOrderGenerator orderGenerator, IOrderProcessor orderProcessor, Address address)
  12:     {
  13:         this.orderGenerator = orderGenerator;
  14:         this.orderProcessor = orderProcessor;
  15:         this.address = address;
  16:     }
  17:  
  18:     public Address Address
  19:     { 
  20:         get
  21:         {
  22:             return address;
  23:         }
  24:         set
  25:         {
  26:             if (newAddress.City != "New York City")
  27:                 throw new ArgumentException("We do not service customers in your area, sorry!");
  28:  
  29:             address = newAddress;
  30:         }
  31:     }
  32:  
  33:     public OrderResult PlaceOrder(IEnumerable<LineItem> lineItems)
  34:     {
  35:         var orderInformation = orderGenerator.CreateOrderFromLineItems(lineItems);
  36:         return orderProcessor.ProcessOrder(orderInformation);
  37:     }
  38: }

With this implementation, it seems very easy to place an order with code like this:

customer.PlaceOrder(lineItems);

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:

   1: class Customer
   2: {
   3:     private Address address;
   4:  
   5:     public Customer() : this(new Address())
   6:     {
   7:     }
   8:  
   9:     public Customer(Address address)
  10:     {
  11:         this.address = address;
  12:     }
  13:  
  14:     public Address Address
  15:     { 
  16:         get
  17:         {
  18:             return address;
  19:         }
  20:         set
  21:         {
  22:             if (value.City != "New York City")
  23:                 throw new ArgumentException("We do not service customers in your area, sorry!");
  24:  
  25:             address = value;
  26:         }
  27:     }
  28: }

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:

   1: class OrderService
   2: {
   3:     private readonly IOrderGenerator orderGenerator;
   4:     private readonly IOrderProcessor orderProcessor;
   5:  
   6:     public OrderService(IOrderGenerator orderGenerator, IOrderProcessor orderProcessor)
   7:     {
   8:         this.orderGenerator = orderGenerator;
   9:         this.orderProcessor = orderProcessor;
  10:     }
  11:  
  12:     public OrderResult PlaceOrderForCustomer(Customer customer, IEnumerable<LineItem> lineItems)
  13:     {
  14:         var orderInformation = orderGenerator.CreateOrderForCustomer(customer);
  15:         orderInformation.AddLineItems(lineItems);
  16:         return orderProcessor.ProcessOrder(orderInformation);
  17:     }
  18: }

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:

orderService.PlaceOrderForCustomer(customer, lineItems);

Summary

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

  1. gravatar

    # by digid - May 24, 2009 at 4:12 PM

    This is the way I program but I'm curious if you've ever read Fowler's article on the Anemic Domain Model:

    http://martinfowler.com/bliki/AnemicDomainModel.html

  2. gravatar

    # by Michael Brennan - May 24, 2009 at 6:17 PM

    I have, DDD services are not the services Fowler speaks of in ADM. The anti-pattern is to extract the behavior into services that form a separate layer on top of the domain model layer. This is bad, since we're now expressing domain logic outside of the domain model layer. Furthermore, this service layer starts to become fat, instead of being kept thin.

    It's also important to keep in mind that most behavior should be encapsulated inside entities and value objects. DDD services should generally be used for interaction between the two. Too much behavior in a DDD service should be a design smell as well.