Blog Archive

Thursday, 14 November 2013

Entities vs Value Objects and Doctrine 2

Update (4th June 2014): Value Objects can now be used in Doctrine 2.5 (currently still in Beta), using the new 'Embeddables' feature: http://doctrine-orm.readthedocs.org/en/latest/tutorials/embeddables.html. An example is given at the end of this post.

An aspect of Domain Driven Design (DDD) that I find quite appealing is the differentiation between entities and value objects. I don't think you even need to embrace DDD as a whole to benefit from this distinction (I'm not saying you shouldn't embrace DDD, just that this aspect of it stands on its own). If you are using Doctrine 2 though, there is no native support for value objects (not yet anyway), which leaves you with the problem of mapping them yourself. In this post I will talk a bit about the difference between entities and value objects and give one suggestion of how to handle value objects when using Doctrine 2.

What is an entity?

An entity is a class that represents something which has an identity independent of its properties. In other words, even if some of its properties change, its identity remains the same. Entities normally represent the fundamental building blocks of your application.

For example, a Customer would be an entity. A customer record might have various properties like name, address, email address, order list, etc., but even if you change one of those values (eg. if the customer moves house), it is still the same customer.

What is a value object?

A value object is a class (usually small, with just a few properties, and frequently used), that represents a value which has no identity separate from its properties. If any one of its properties change, it is no longer the same value.

An example of a value object is an address. If you change any part of an address, it becomes a different address. Another example would be a price - it might have properties for currency, amount, and payment frequency, but changing any of those would yield a new price.

As such, value objects are immutable - their properties cannot change (it is normal to use getters but not setters for the properties of value objects). If two value objects have exactly the same properties, they can be regarded as equal.

Value objects can have methods, but the behaviour of a method doesn't ever change the object's state. If a method needs to change a property, it will return a brand new instance of the value object instead of modifying the existing one. Methods on a value object can also return other types of value of course (eg. a boolean return value would be typical for a validate method).

Can a Value Object have an Entity as a property?

It doesn't happen very often, but there is nothing wrong with referencing an entity from a value object - having a reference to an entity does not force a value to become an entity. For example, an address value object would typically include a country property. Whilst this would normally be a string, your application might need to have a Country class which has mutable properties (such as whether the country is a member of the EU) and is therefore an entity. But you could still have a country property on your address value object, even if it refers to an entity (I will use this example in the sample code below).

Can an address/price/date range/etc. ever be an Entity?

Yes! Whether something is an entity or a value object depends on how you intend to use it. If you were writing an application for a postal service or a town planner, an address might be more than just a value - you might have the power to change properties of an address without changing its identity (eg. if you can assign house numbers or postcodes).

Why use value objects?

This is really two separate questions, one of which is a little easier to answer than the other...

Why use value objects instead of scalar values?

This is the easy one. You could have a Customer class with properties for address_line_1, address_line_2, etc. - each as a string. Or a phone number could be held as a string with the area code in brackets. By collecting these properties together into a single value object, or separating a string into constituent parts as separate properties of a value object, it is not hard to see that you will gain many OOP benefits. If there are several pieces of data that go together to make a single domain concept, they belong in an object. You can then perform computations on that discrete set of data, validate it, re-use it, and extend it. It also allows you to separate concerns - your entity does not have to worry about any domain logic relating to the value, leading to cleaner code.

Why use value objects instead of Entities?

Why not just give everything an ID and make it an entity? There's no technical reason why you couldn't have an address ID for example, and even allow the properties to be modified. However, although at first glance it might seem as though separating out entities and value objects makes things more complicated, it actually makes things simpler. Why give an address an ID if it doesn't need one? It unnecessarily complicates your model, and could also cause confusion as it is not clear how you intend the data to be used. If something is defined as a value object, we know what it is, that it will not change, and that another object of the same type with the same properties is considered equal to it. This gives us lots more information on how it is meant to be used - so the conceptual difference is valuable in making your code understandable and easy to maintain.

Having said that, if you are using an ORM which does not support value objects (such as Doctrine 2), there is indeed some additional complexity involved, as you need to provide some way of mapping custom value objects to database columns yourself. If you can do this in a way that will be easy to refactor later (when hopefully the next version of Doctrine will support value objects), it might still be worthwhile doing that extra legwork while defining your entities so that you can reap the rewards of improved code clarity and usability.

Using Value Objects with Doctrine 2

Let's take a simplified example. Here is a basic Customer class, including phpDoc annotations for Doctrine 2 (the BaseEntity class it derives from [not shown] contains magic methods __get and __set for accessing the protected properties, as Doctrine 2 doesn't like you to use public properties):

namespace Entities;
/**
* @Entity @Table(name="customer")
* @property int $id
* @property string $name
* @property \ValueObjects\Address $address
* @property string $email_address
* @property string $telephone
**/
class Customer extends BaseEntity
{
    /** @var integer @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @var string @Column(type="string") */
    protected $name = '';
    /** @var \ValueObjects\Address */
    protected $address;
    /** @var string @Column(type="string", length=150) **/
    protected $email_address = '';
    /** @var string @Column(type="string", length=40) **/
    protected $telephone = '';
}

Here we have an Address value object as one of our properties, but we can't map it directly to a column, because it is made up of several fields and Doctrine 2 currently won't resolve them for us. We could give Address an ID and make it an entity, and Doctrine 2 would then be able to map it, but if we want to use a value object, we have to do our own mapping, which I will come to in a minute. First, here is the Address class (the BaseValueObject it derives from has a magic getter but no setter, so the properties are read only):

namespace ValueObjects;
class Address extends BaseValueObject
{
    /** @var string **/
    protected $line_1 = "";
    /** @var string **/
    protected $line_2 = "";
    /** @var string **/
    protected $line_3 = "";
    /** @var string **/
    protected $town = "";
    /** @var string **/
    protected $state = "";
    /** @var string **/
    protected $postcode = "";
    /** @var \Entities\Country **/
    protected $country;

    public function __construct($line_1 = '', $line_2 = '', $line_3 = '',
                                $town = '', $state = '', $postcode = '',
                                \Entities\Country $country = null)
    {
        $this->line_1 = $line_1;
        $this->line_2 = $line_2;
        $this->line_3 = $line_3;
        $this->town = $town;
        $this->state = $state;
        $this->postcode = $postcode;
        $this->country = $country;
    }
}

As described above, the country property of the Address value object here refers to an entity (although it could very well refer to another value object, or just a scalar value).

Now, to enable Doctrine 2 to persist our Customer class, with its Address value object, we will need to tell it how to map to columns to hold each part of the address. We could use a custom mapping type for this, but there is another way which is probably simpler and a little more generic.

As Address is not an entity, and has no identifier (ie. no primary key), the address data cannot be held in a separate table - it will have to go in the same table as the Customer entity. We could therefore just add the fields to our Customer entity and map them to an accessor like this (note that here we do have a setter as well as a getter for the address, because the Customer entity is not immutable):

namespace Entities;
/**
* @Entity @Table(name="customer")
* @property int $id
* @property string $name
* @property \ValueObjects\Address $address
* @property string $email_address
* @property string $telephone
**/
class Customer extends BaseEntity
{
    /** @var integer @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @var string @Column(type="string") */
    protected $name = '';
    /** @var \ValueObjects\Address */
    protected $address;
    /** @var string @Column(type="string", length=150) **/
    protected $email_address = '';
    /** @var string @Column(type="string", length=40) **/
    protected $telephone = '';

    /** @var string @Column(type="string", length=100) **/
    private $address_line_1 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_line_2 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_line_3 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_town = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_state = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_postcode = "";
    /**
    * @ManyToOne(targetEntity="Country", fetch="EAGER")
    * @JoinColumn(name="address_country", referencedColumnName="code")
    * @var Country
    **/
    private $address_country;

    public function setAddress(\ValueObjects\Address $address)
    {
        $this->address = $address;
        $this->address_line_1 = $address->line_1;
        $this->address_line_2 = $address->line_2;
        $this->address_line_3 = $address->line_3;
        $this->address_town = $address->town;
        $this->address_state = $address->state;
        $this->address_postcode = $address->postcode;
        $this->address_country = $address->country;
    }

    /** @return \ValueObjects\Address **/
    public function getAddress()
    {
        if (!isset($this->address)) //Lazy load
        {
            $this->address = new ValueObject\Address(
                    $this->address_line_1,
                    $this->address_line_2,
                    $this->address_line_3,
                    $this->address_town,
                    $this->address_state,
                    $this->address_postcode,
                    $this->address_country);
        }
        return $this->address;
    }
}

Here we have private properties to map the individual parts of the address to database columns without exposing these to the outside world (the magic getter in the base class will not have access to them), but we have a public getter and setter for the address value object.

This will work, but it doesn't look very nice - our Customer class is now bloated with address mapping code. Also, we might have other entities that also need an address, and we don't want to have to copy and paste this all over the place. This is where traits come in handy! We can use a trait to keep the nasty mapping kludge out of our entity, and re-use it wherever an address is needed:
namespace Traits;
/**
* This trait allows us to map address value objects to database columns using Doctrine 2
*/
trait Address
{
    /** @var string @Column(type="string", length=100) **/
    private $address_line_1 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_line_2 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_line_3 = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_town = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_state = "";
    /** @var string @Column(type="string", length=100) **/
    private $address_postcode = "";
    /**
    * @ManyToOne(targetEntity="Country", fetch="EAGER")
    * @JoinColumn(name="address_country", referencedColumnName="code")
    * @var Country
    **/
    private $address_country;

    public function setAddress(\ValueObjects\Address $address)
    {
        $this->address = $address;
        $this->address_line_1 = $address->line_1;
        $this->address_line_2 = $address->line_2;
        $this->address_line_3 = $address->line_3;
        $this->address_town = $address->town;
        $this->address_state = $address->state;
        $this->address_postcode = $address->postcode;
        $this->address_country = $address->country;
    }

    /** @return \ValueObjects\Address **/
    public function getAddress()
    {
        if (!isset($this->address)) //Lazy load
        {
            $this->address = new ValueObject\Address(
                    $this->address_line_1,
                    $this->address_line_2,
                    $this->address_line_3,
                    $this->address_town,
                    $this->address_state,
                    $this->address_postcode,
                    $this->address_country);
        }
        return $this->address;
    }
}

Putting the ugly stuff there in the trait means that the only pollution in our entity class is a use statement:
namespace Entities;
/**
* @Entity @Table(name="customer")
* @property int $id
* @property string $name
* @property \ValueObjects\Address $address
* @property string $email_address
* @property string $telephone
**/
class Customer extends BaseEntity
{
    use \Traits\Address; //Allows Doctrine 2 to map the Address value object

    /** @var integer @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @var string @Column(type="string") */
    protected $name = '';
    /** @var \ValueObjects\Address */
    protected $address;
    /** @var string @Column(type="string", length=150) **/
    protected $email_address = '';
    /** @var string @Column(type="string", length=40) **/
    protected $telephone = '';
}

The fly in the ointment

Aside from the fact that manually mapping fields is a bit of a kludge in itself, there is another problem here. What about cases where an entity needs more than one address? Perhaps a billing address and shipping address for example? We can't include the trait twice (well, we could use aliasing, but both would refer to the same values and would require a monumental kludge to differentiate), so we would have to add more traits for the different address types. I still think that is better than putting it all directly in the entity class, but it does smell a bit iffy. What do you think? Is there a better way?

Update: Using Embeddables

If you can use Doctrine 2.5, this problem is easily solved using Embeddables. Here is how you would do the mapping for a customer address in Doctrine 2.5:

namespace ValueObjects;

/** @Embeddable **/
class Address extends BaseValueObject
{
    /** @var string **/
    protected $line_1 = "";
    /** @var string **/
    protected $line_2 = "";
    /** @var string **/
    protected $line_3 = "";
    /** @var string **/
    protected $town = "";
    /** @var string **/
    protected $state = "";
    /** @var string **/
    protected $postcode = "";
    /** @var \Entities\Country **/
    protected $country;
 
    public function __construct($line_1 = '', $line_2 = '', $line_3 = '',
                                $town = '', $state = '', $postcode = '',
                                \Entities\Country $country = null)
    {
        $this->line_1 = $line_1;
        $this->line_2 = $line_2;
        $this->line_3 = $line_3;
        $this->town = $town;
        $this->state = $state;
        $this->postcode = $postcode;
        $this->country = $country;
    }
}


namespace Entities;
/**
* @Entity @Table(name="customer")
* @property int $id
* @property string $name
* @property \ValueObjects\Address $address
* @property string $email_address
* @property string $telephone
**/
class Customer extends BaseEntity
{
    /** @var integer @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @var string @Column(type="string") */
    protected $name = '';
    /** @var \ValueObjects\Address @Embedded(class="\ValueObjects\Address") */
    protected $address;
    /** @var string @Column(type="string", length=150) **/
    protected $email_address = '';
    /** @var string @Column(type="string", length=40) **/
    protected $telephone = '';
}
Painless!