There is never a collection of Value Objects

Adding to a Value Object property always adds to it, never adds another instance to it.  Distinguishing Value Objects by maintaining a collection of them implies identity.

It is a mistake to attempt to maintain a collection of Value Objects.

Let’s take the near-canonical Address, a perfectly valid Value Object as described on page 98 of the Big Blue Bible:

public class Address : ValueObject<Address>
{
    public virtual string StreetAddress1 { get; set; }
    public virtual string StreetAddress2 { get; set; }
    public virtual string City { get; set; }
    public virtual State State { get; set; }
    public virtual string ZipCode { get; set; }
    public virtual Country Country { get; set; }
}

And a perfectly valid usage of this Address Value Object is as a component of an Entity:

public class Customer : Entity
{
    public Address WorkAddress { get; set; }
    public Address HomeAddress { get; set; }
    // ...
}

This, on the other hand, is incorrect:

public class Customer : Entity
{ private readonly ISet<Address> _addresses = new HashedSet<Address>(); public Address[] GetAddresses() { return _addresses.ToArray(); } }

Again, distinguishing addresses by maintaining a collection of them implies identity. If you tried to edit one of the addresses respecting some immutable Value Object oriented thinking, you would clear the collection, find (somehow) the one you intended to edit, replace it along with all the others.  This is not possible.  When you only care about the values of an object, rather than a key, you cannot find the original one you intend to edit from an instance of an edited object.  If you care about a key you have an Entity.

The original and the edited will never match. You might try to give the Value Object a database key so that it is easy to find.  But if you do that, why again are you clearing all the rest and re-adding them?  That doesn’t make sense – just edit it.

There’s another problem.  In the “wallet” example you have paper money in your wallet, several pieces of paper.  Your wallet is the Entity and the money is a Value Object.  If you were to take some money out of your wallet, you would subtract from the Value Object, not remove an instance of the Value Object from a collection.  You don’t care if you have five ones or one five, you just care that you have five.  Value Objects never collect, they only aggregate.

If you care about each individual instance, you have an Entity.

The best way to model something like this is to use an Entity that has a Value Object component.  If I am maintaining a collection of Addresses, I do actually care that each is different – the trick is to find out why.  In my case I care because the Customer has an address history and I wish to maintain that history.  I model that like this:

public class HistoricalAddress : Entity
{
    public Address Address { get; set; }
    public DateTime? Start { get; set; }
    public DateTime? End { get; set; }
    public AddressType Type { get; set; }
}

Now the HistoricalAddress Entity lives in the Customer Aggregate…

public class Customer : Entity
{
    private readonly ISet<HistoricalAddress> _addresses = new HashedSet<HistoricalAddress>();

    public HistoricalAddress[] GetAddresses()
    {
        return _addresses.ToArray();
    }

    public HistoricalAddress GetAddress(Guid historicalAddressId)
    {
        return _addresses.SingleOrDefault(x => x.Id == historicalAddressId);
    }

    //...
}

… and I am free to edit, list, and delete them with ease.

The one caveat is when you are maintaining a collection of Value Objects that represent all possible values.  If you have a State Value Object you may want to represent a collection of all 50 U.S. States as a select list in the user interface.  This works fine and is programmatically possible, until you are interested in maintaining this collection.

About these ads
This entry was posted in Domain-Driven Design and tagged , , . Bookmark the permalink.

16 Responses to There is never a collection of Value Objects

  1. Casey says:

    A collection of value objects in your address example is fine… Amazon use this model, at least it appears externally, and I have done so in econmerce systems

    A cutsomer has a list of adddress objects with their account,not with identity, that are copied upon selection to the delivery or invoice addresses, changing the delivery or invoice address manually does not modify an existing one, it creates a new one

    No identity is required

  2. Matt says:

    @Casey: Now try to delete one. Do you have a method Entity.DeleteAddress(string street, string street2, string city, State state, Country country, string zip) and then do a Linq call to find the address by value? Are you trying to kid yourself that you don’t care about *that particular address*?

    If you use this feature at amazon, “manage address book”, you will notice the addresses have identity very, very clearly. each one is preceded with a number making it distinct and when you submit the delete request an addressId is transferred to the server. when you delete number one, you are deleting *that one*.

  3. Pingback: Arjan`s World

  4. Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #262

  5. Pingback: Interesting Finds: 2009.01.12 - gOODiDEA.NET

  6. Say I have a profile page where I can save my list of favorite colors. Each color I add is clearly a value object (RGB 50, 60, 80) but the order in which I add it provides a scoped identity. If I want to delete one, I delete the one at index X, and it is removed.

    You also have to consider whether or not this “value object” is really an entity in disguise.

  7. Matt says:

    Ben, not sure if you are agreeing with me or not. But I’ll continue to rant anyway. =)

    Scoped identity is still identity. Sounds like you have a collection of ColorPreference Entities in a User Aggregate. You still access ColorPreference through the Aggregate (not through the repository), similar to how you would do it “by index”.. “By index” doesn’t really work anyway though. Consider sort order – it will change from screen to screen. The counter-argument is, “yeah but I specify the index on the screen, so that even if it’s sorted differently I still know which one is which.”

    The counter-counter argument is a) that’s an identity even if you are shoving it into an index and b) whenever you care about a specific instance, “which one is which”, you have an Entity!

  8. paul says:

    “never” I think is an overstatement and while I agree with most of your analysis for the business case you present, I don’t think that you can say that you’ve conclusively proven that no other case can possibly exist.

    The example I’d give is tags, or keywords, or the like. I can choose to refer to them in my ‘blogpost’ entity as a string array, but they’re still not individually important. I likely would just replace them all upon update rather than specifically editing one in the collection, because I don’t care that much about the keyword itself so much as I care about the fact that a given blogpost is described.

  9. Matt says:

    Paul, I think that’s a good example until you try to maintain the collection (by editing or deleting one). I would still model it differently, with Tag being an Aggregate Root itself.

  10. ryzam says:

    I think the identity that you are referring is a technical identity with the database. I still believe that we can have a collection of value object. If you said that Address is the entity and has their own Id does that Id value is useful within the business or that id only there because of database relation technicality?

  11. Colin Jack says:

    Not sure I agree with you on this.

    “If you tried to edit one of the addresses respecting some immutable Value Object oriented thinking, you would clear the collection, find (somehow) the one you intended to edit, replace it along with all the others.”

    This doesn’t make sense to me, why am I clearing the collection. Lets say I want to replace the home address, I’d just call ReplaceHomeAddress and it’d replace the Address with an AddressType of Home or it would replace the appropriate item in the dictionary (assuming it was a dictionary). If, when identifying an address, I’m only interested in one piece of its data (Home vs. Work) then thats fine, can’t think why its a problem?

    “If you were to take some money out of your wallet, you would subtract from the Value Object, not remove an instance of the Value Object from a collection.”

    The wallet is an entity so why not just subtract money (value objects) from it?

    “You don’t care if you have five ones or one five, you just care that you have five.”

    If it isn’t important to the system then thats fine, it is important in real life if you have a $5 note and you need to hand over $2 of course.

    “If I am maintaining a collection of Addresses, I do actually care that each is different – the trick is to find out why. In my case I care because the Customer has an address history and I wish to maintain that history”

    I agree with this, relationship objects are very much under-explained in DDD (http://colinjack.blogspot.com/2008/10/ddd-making-meaningful-relationships_28.html). However as well as temporal aspects you probably also care about different types of addresses, just so happens you’d handle them differently.

  12. Matt says:

    Colin,

    If you have a method named ReplaceHomeAddress you aren’t maintaining a collection. Maybe you are internally, but that’s no different than having a HomeAddress property.

  13. Colin Jack says:

    Sure, you could have a HomeAddress property to/instead of.

  14. Pingback: More On Value Objects

  15. Sosh says:

    Matt – Completely agree with you here. I’ve grappled with the same issue myself, and have also come to the conclusion that a collection of value objects is nonsensical.

  16. Pepito says:

    The problem with DDD is that it doesn’t follow a set of steps, or rules or anything. It all comes from people’s perspective. You find hundreds of blogs talking about the same subject and they all have different views about the same thing. It’s an ocean of contradictions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s