Domain Driven Design (DDD): An Exercise in Value Objects

I'm reading the Eric Evans book on DDD and wanted to catalogue some notes as I'm going through it to help me clarify and solidify what I'm picking up from the book.

DDD, as I understand it, encourages programmers to build a rich domain model of a system that is separate from the user interface and infrastructure (database, etc.) of the system. The domain model is made up of three types of components: Entities, Value Objects, and Services.

The distinction between Entities and Value Objects is a bit of a subtlety. Here are some characteristics of something that is a good Value Object:

  • They have no conceptual identity.
  • They describe a characteristic of a thing.
  • They're used when we only care about what, not who or which.
  • They should be treated as immutable.
  • If you feel a need to create a bidirectionally navigable association between two value objects, one of them probably isn't a value object.

An Example

A Person object in most systems is a clear Entity. An Address object attached to a Person would typically be a Value Object.

Here are some statements in a logical progression:

  1. A Person object has an Address property on it.
  2. Any instance of an Address property is immutable: once it's instantiated its attributes cannot be changed.
  3. When adding a new Person into the system, the user will specify the typical address fields. When the user hits the Save button we'll instantiate the Person, then newPerson.Address = new Address(text values from UI)
  4. Somewhere down the stack this will be persisted to a database. Two options here:
    • The fields of the Address object could be stored as columns on the Person table. This is a good solution for many situations, but it's also pretty easy to think through, so we'll choose the next option for this example.
    • Addresses are stored in an Address table with a surrogate key that is referenced by the AddressId column of the Person table.
  5. With our persistence choice, when saving a new Person two inserts are required, one record into Address and one into Person.
  6. This persistence choice also leads us to add an AddressId property on the Address object.
    • If Address is a true Value Object, nothing besides the persistence infrastructure should care about AddressId. If anything else does care about it there will be trouble in the last point of step nine below.
  7. When it's time to retrieve a Person, Address is easily populated from the database through a join, multiple selects, or a lazy load.
  8. Things get interesting if Address is displayed in an editable form and a user makes a correction.
    • Since Address is immutable we cannot say myPerson.Address.City = cityTextbox.Value
    • Instead we say myPerson.Address = new Address(text values from UI) again, just like during creation.
    • Note that we no longer have any object that references the old AddressId. It's fair game for the garbage collector like an old sofa on the curb during cleanup week.
  9. When the Person is saved the persistence framework needs to figure out that the following should occur:
    • Insert the new Address with a new AddressId.
    • Update the Person record's AddressId column.
    • Delete the old Address.

So I would guess the NHibernate can configure a relationship to work in this way, but I've not verified it.

Another Example

Let's try one more example of managing an association between an Entity and a Value Object:

  1. A Person has a Status (current, inactive, whatever, etc.). Person is an Entity, Status is a Value Object.
  2. The list of available Statuses is maintained by the user and stored in the database.
  3. This time when a Person is created and a Status set, the persistence framework would not need to create the record since it already exists.
    • I'm not exactly sure what the best way to get an instance of the desired Status, but I don't think it matters in this example.
  4. When updating a Status, the attributes of a Status are still immutable, like all well-bred Value Objects should be, so updates are done through Person.Status = someStatus.
  5. The persistence framework should know that it does not delete the old status from the table, nor insert the new status into the table.

Okay, I've satisfied myself that what I understand about Value Objects is definitely doable with the tools and technologies I'm familiar with.

Comments !