Saturday, 15 September 2012

Entities, Value Objects and Services

In chapter five A Model Expressed in Software of Domain-Driven Design, Eric Evans wrote about ENTITIES, VALUE OBJECTS and SERVICES. These are three important patterns of DDD. They help us to capture important concepts of the domain. By classifying the objects in this way we can make objects less ambiguous. In this post I write down my understanding about this topic.

So what is an ENTITY?

ENTITY = IDENTITY + CONTINUITY

ENTITY is an object that has distinct identity and also has continuity. This type of object is not fundamentally defined by its attributes. For example: two customers can have same name, age or even living in same address but they must have unique identifier (say, customer number) within the system.



ENTITY has a life cycle that can change its form and content over the time but a thread of continuity must be maintained. Here identity plays an important role because ENTITY can be tracked effectively with the help of it.



Care must be taken when generating identity for an ENTITY. Identity must be guaranteed to be unique within the system no matter how the system is developed, even whether it is distributed or not. Once generated, it must not be changed. Sometimes single data attribute (guaranteed to be unique) can be identity of an entity. For example: order number, transaction number, account number, customer number etc. Sometimes you need a combination of data attributes to define an identity. For example: daily newspapers might be identified by the name of the newspaper, the city, and the date of publication.

What about VALUE OBJECTS?

VALUE OBJECT does not have a conceptual identity but it represents a descriptive aspect of the domain.For example: A customer may be modelled as an ENTITY with an identity, but his phone number is a VALUE OBJECT.

VALUE OBJECTS help us to design and code in a better way:
  • They make implicit concepts explicit.
  • They help to write a clear service api.
  • They take the responsibility of data validation and error handling.
  • They help us to write a well testable and maintainable code.
Take the example of a phone number. We can declare it's type as string in customer class.

public class customer {  
  ...... 
  private string phoneNumber;
  ........ 
}

But if you think carefully, declaring phoneNumber as a string does not say much about it. Here the phoneNumber is implicit. It may introduce bugs, make you to write awkward and duplicate code. When you make it explicit, things become more clear:

public class customer {  
  ...... 
  private PhoneNumber phoneNumber;
  ........ 
}

It also opens option to add different behaviors to PhoneNumber. You can add data validation and error handing to this object. Here is the sample code of PhoneNumber [2]:

public class PhoneNumber {

  private final String number;
  
  public PhoneNumber(String number) {
     if(!isValid(number))
    throw ...
     this.number = number;
  }
  
  public String getNumber() {
    return number;
  }
  
  static public boolean isValid(String number) {
    return number.matches("[0-9]*");
  }
  
  public String getAreaCode() {
    String prefix = null;
    for (int i=0; i< number.length(); i++) {
 String begin = number.subString(0,i);
        if(isAreaCode(begin)) {
    prefix = begin;
    break;
        }
    return prefix;
  }

  private boolean isAreaCode(String prefix) { ... }
}
If you noticed carefully, you will find that we have put computational complexity in this object rather than putting it in service layer. So the service layer has less burden. Code duplication is reduced. Less code means less bug. Now you can write a set of junit testcases for this object.

Lets see how VALUE OBJECTS help us to write clear service api. For example: we have a api that takes name, age, address and phone number to add a customer:

void addCustomer(String, Int, String, String);

Is the above api clear to you? You can see String and Int all over, nothing meaningful. How about writing this way:

void addCustomer(Name, Age, Address, PhoneNumber);

Now the api is readable. Even a non technical person can understand the above method signature.One thing we need to remember - VALUE OBJECTS should be immutable. For example:

Money money1 = new Money("EUR", 30);
Money money2 = new Money("EUR", 40);
Money money3 = money1.add(money2);

When you add money2 to money1, you are not altering money1, instead returning a new Money object (assigned to money3) which represents the two amounts of Money added together. Ensuring immutability of value object is important if you want to share it safely. It cannot be changed except by full replacement.

Services

A SERVICE is a standalone domain operation that you cannot fit in an ENTITY or VALUE OBJECT. It is defined purely in terms of what it can do for a client.

Evans mentioned in his book that a good SERVICE should have three characteristics:
  • The operation relates to a domain concept that is not a natural part of an ENTITY or VALUE OBJECT
  • The interface is defined in terms of other elements of the domain model
  • The operation is stateless

SERVICES can be partitioned based on layer:
  • APPLICATION SERVICES sit above the domain services, handle cross cutting concern such as transaction, security. They also talk to the presentation layer to get the input or send the output back.
  • DOMAIN SERVICES deal with business logic that cannot live in an ENTITY. For example, transferring fund between two accounts.
  • INFRASTRUCTURE SERVICES are those service that are more technical in nature, for example sending out an email or SMS text message.

Operation names in SERVICES should come from the UBIQUITOUS LANGUAGE. Parameters and results of these operations should be domain objects to make them explicit. SERVICES should be used carefully, don't take away all the behaviors from ENTITIES and VALUE OBJECTS and put them in SERVICES.

References:

1. Domain-Driven Design by Eric Evans
2. Power Use of Value Objects in DDD by Dan Bergh Johnsson
3. EvansClassification by Martin Fowler

No comments:

Post a Comment