I’m sat in a railway station in the French Alps, waiting for the TGV to Paris, with nothing much to do except eat crisps and watch Oggy and the Cockroaches on French TV. They say good writing draws inspiration from the world around it, so – because Oggy and the Cockroaches defies all rational discussion – let’s talk about crisps. Ed left a comment on my “lazy loading lunchbox” post that got me thinking about the semantics of value objects – what exactly is a value object, and what role would it play in the whole rucksack/lunchbox scenario.
Ruck sack = Aggregate Root. RuckSackRepository is the only mechanism for retrieving the object graph. The CrispBag is an Entity and of course has no repository. The Crisp Bag is just an array / collection of Crisps, which are in turn Value Objects.
On careful reflection, I don't think a crisp is a value object – at least, not in the playground scenario I was dealing with - because I'd argue that every crisp has a distinct identity and lifecycle. Don't believe me? Here, have some crisps. No, it's OK, go on, have the whole bag... <crunch, crunch, crunch> Now that you're happily tucking into your crisps - guess what? Colin licked one of those crisps earlier when you weren't looking. Suddenly the question of "which crisp?" becomes extremely important. Is the contaminated trouser-crisp one of the handful left safely in the bag - or have you already eaten it? If we model crisps as value objects, we have no way of telling. We can tell how many crisps are left, sure, but we can’t distinguish between them, and we can’t model any operation that would affect the state of a single crisp whilst leaving the other crisps unaffected.
OK, so if a crisp isn’t a value object, then what is? I think that’s a much harder question to address, because as with so many patterns, the answer depends on the scenario you’re modelling.
The value object pattern is typically used to model quantities, descriptions and measurements, where two objects can be considered equal if they have the same value (state). Two classic examples are dates and money. In most domain models, “January 2nd, 1978” will always refer to exactly the same thing – a particular 24-hour period sometime in the late 1970s – so you can model your date/time fields as value objects (remembering to include time zone information if necessary). If you and I are both born on 2nd January 1978, then it’s valid to assume we have the same birthday, because that date can only refer to one thing. (By way of comparison, if your father and my father are both called Dave Beattie, we’re not necessarily related, because there could be any number of people with that name in our domain model)
Money is interesting, because money is a value object by law in most economies - that's how it's treated in business and in court. In legal terms, this is known as fungibility. Point is, when we ask “does this credit equal that debt?”, we’re only interested in the amount. You can repay a £10 debt with any ten pounds as long as the numbers add up; you don't need to track the specific ten-pound-note that was originally borrowed. When you deposit £50 at the bank, you can't go back a week later and ask if that particular fifty pounds is still there - as soon as you deposit it, it becomes part of your account balance and ceases to exist as £50 in its own right.
Remember, though, that value objects are just one modelling pattern, and you need to be sure that you’ve chosen the right pattern for your particular scenario. If you're writing a system for the CIA to track marked dollar bills, you'll need to model those bills as entities so you can track individual bills (e.g. using their serial number). Point is, when you do this, you're not treating them as currency, you're treating them as artifacts. The same would apply to an antiques house dealing in rare coins - it's not the face value of the coins that matters, it's their age, rarity and historical significance that's important. If you’re writing a game where the player can travel through an infinite number of parallel universes, you might need to track an infinite number of different instances of “January 12th, 1978”.
If it’s not clear how to model a particular element in your model, try asking “which one?” If the question makes sense within your own scenario:
Me: “Colin licked one of those crisps!”
You : “Which one?”
Me: “Hey, says here this house was once owned by Chris Columbus!”
You : “Which one?”
- then you’re probably dealing with entities. If the question “which one” is meaningless in the context of your domain:
Me: “I only paid £1 in income tax last year!”
You: “Oh yeah? Which one?”
Me: “I was born on January 12th, 1978”
You: “Really? Which one?”
- then you’re probably better off modelling the subject of the question as a value object.