Domain Driven Design: A Step by Step Guide - Part 2

This is the second part in a series on Domain Driven Design. You can read the first part here. This article was created and edited (with permission) from a series of posts on Casey's blog.

Entities and Value Objects

Entities and Value Objects (VO) form the core building blocks of Domain Driven applications. Why has it taken this long to get to something so fundamental? Back in the good old days we used to have things called business objects, these were classes that held some data, had some methods, and we threw into a database.

DDD has refined this concept a little, by splitting the idea of these business objects into two distinct types, Entities and Value Objects

Entities

“this is my Entity, there are many like it, but this one is mine”

The key defining characteristic of an Entity is that it has an Identity – it is unique within the system, and no other Entity, no matter how similar is the same Entity unless it has the same Identity.

Identity can be represented in many ways on an Entity – it could be a numeric identifier (the classic CustomerID), it could be a Guid (the classic … oh never mind), or it could be a natural key (like the CustomerNumber your Customer was given by your CRM system when they first bought from you).

Examples of common Entities are: Customer, Product, Container, Vehicle

Whichever way you choose to represent it, an Entity is defined by having an Identity.

Value Objects

The key defining characteristic of a Value Objects is that it has no Identity. Ok, perhaps a little simplistic, but the intention of a Value Object is to represent something by it’s attributes only. Two VOs may have identical attributes, in which case they are identical. They don’t however have any value other than by virtue of their attributes.

Another aspect common to VOs is that they should probably be immutable, once created they cannot be changed or altered. You can create a new one, and as they have no identity, that is just the same as changing another one.

Examples of common Value Objects: Money, Address, ProductCode

Hey, I’ve Heard of These, We Have Them in .Net!

Don’t confuse Entities and Value Objects with Reference Types and Value Types, they sound similar but bear only a passable resemblance. Both Entities and Value Objects would be usually be implemented in .Net as Reference Types (Class not Struct).

So Entities and Value Objects Just Store Data?

Definitely not! The point of DDD is to create a rich Domain – if you only stored data in your Entities and Value Objects, you would have an anaemic domain model – one of the primary anti-patterns to DDD.

Entities should have methods on them that logically belong there – so a Customer might have a .UpgradeToPreferredStatus and a VO representing Money may have a methods to .Add, .Subtract, and so on. These methods would obviously come from the Ubiquitous Language (“we can Upgrade a Customer to Preferred Status”, “we can Add two amounts of Money together”)

In fact, in DDD there is less concern about what or how the your Entities and VOs store data, and more concern about how they represent that data and how they manipulate that data.

Some would go so far as to remove Setters and Getters from them entirely – though my personal preference is to remove Setters only, and make Getters representative of the outside “shape” of the Entity.

For VOs create only constructors, after all they are meant to be immutable.

For Entities use only constructors or strong methods to make any changes. Directly changing properties can have nasty side effects, and leave Entities in a temporarily invalid state.

And We Put These Things Into Repositories Right?

Not quite, at least not all of them directly, we will cover that in the next part of the series, the concept of Aggregate Roots.

In Conclusion

The “”things” in your business will usually be represented by Entities and Value Objects in DDD. They form the shape of your Domain, and would generally be the things you draw up on that class diagram on the wall.

Where is the code?

Right about now I can hear murmurs, "I haven't seen any code yet"

That is because I view Domain Driven Design firstly as a design methodology, secondly as an architectural style, and lastly as some great software patterns.

I don't believe I am alone in that view, after all it is a significant way into the book before anything resembling UML appears, and even further before anything code-like is introduced. Now you could pickup the book, extract some of the key terms and half a dozen patterns, and undoubtedly you would be writing better software ... but I suspect you may have missed the "wood for the trees" or put another way "You spent so much time focusing on the details, and missed the bigger picture"

DDD is a better way of thinking about software design, it helps you translate what users and businesses need into software that meets those needs. The patterns may make your software more stable or more maintainable, but it is the methodology that guides you to deliver something fit for purpose.

Of course, eventually you are going to need some code, but I am going to postpone that for a while I am afraid, I apologise in advance if anyone is eager to get typing. We have a few more fundamentals of DDD to explore first, then I promise I will start to delve a little into how to best implement these things in C# code.

Aggregates and Aggregate Roots

We are family
I got all my sisters with me
Sister Sledge

Some things belong together, like Apple Pie and Ice Cream, or Sonny and Cher. And so it is with Entities and Value Objects (VOs) – some of them belong together.

Aggregates are groups of things that belong together. An Aggregate Root is the thing that holds them all together.

I will warn in advance, as I proof read this post, it was pretty complicated – while I have tried to simplify the concepts, I am not certain I have totally succeeded. Hopefully Aggregates will become more clear later in the series when I start exploring them in code.

A Simple Example

In all Object Oriented programming we are used to dealing with objects that have references to other objects, and in DDD it is no different. For example, our Customer *may have a reference to the customer’s *Orders.

An Aggregate is slightly different, where as Customers and Orders can exist in the system independently, some Entities and Value Objects make absolutely no sense without their parent. The obvious example following on is Orders and OrderLines.

OrderLines have no reason to exist without their parent Order, nor can they belong to any other Order. In this case, Order and OrderLines would probably be an Aggregate, and the Order would be the Aggregate Root

The rule of Cascading Delete is sometimes cited as a good way to tell if you have a group of Entities or VOs that should be an Aggregate – if the parent, in this case the Order, was deleted all other parts of that Aggregate below Order would be deleted too. So if it doesn’t make sense that a parent being deleted would also delete all children, then you don’t have an Aggregate, you just have a good old fashioned reference.

So What is the Point of an Aggregate?

Well the first and most obvious point of an Aggregate is that it operates to a large degree as one “thing”. Notably the Aggregate Root is the single Entity that controls access to the children.

Where you may have a Customer with operations like .UpdateToPreferredStatus and you may have an Order with .GetSumTotal – the OrderLines of an Order would not have any logic exposed outside of the Order Entity – in other words to add a new OrderLine, or to change an OrderLine, you would tell the Order to make the changes – Order.AddNewItem for example.

In this respect, Aggregates provide a clean pattern to keep logic where it really belongs.

Another aspect of Aggregate Roots is that they are the Entities that are dealt with by Repositories.

In our examples above, we would have a Customer Repository, and an* Order Repository, but there would not be an *OrderLine Repository. It is the responsibility of the Repository for the Aggregate Root to deal with persistence of all the children of that Aggregate.

Restrictions on Aggregates and Aggregate Roots

The main, and possibly obvious restriction on Aggregate Roots is, they must be Entities, and cannot be Value Objects. Back to the previous post, you will remember that Entities have Identity, and Value Objects do not – you could not ask a Repository to retrieve an Aggregate Root *if it had no *Identity.

Within an Aggregate, the other players can be Entities or VOs as the domain dictates. For example, expanding our Order example further, the Aggregate may comprise the Order (Aggregate Root), the Order may have an OrderNumber (Value Object), some OrderLines (Entities), and a Shipping Address and Billing Address (Value Objects)

Entities can hold references to any Aggregate Root, but never to any other Entity or VO within the Aggregate. To access any other part of the Aggregate, you must navigate from the Aggregate Root.

How the component parts of an Aggregate are persisted is a matter for the implementation behind Repository, but if you were using an ORM like NHibernate for example, the changes are that the Value Objects would be NHibernate Components nested in their parent entity record and the Entities would be old fashioned mappings, each with their own table.

In Conclusion

Aggregates provide a logical grouping of Entities and Value Objects that belong together at all times. An Aggregate Root is the gatekeeper to the Aggregate. Each Aggregate is treated as a single unit for persistence purposes.

By logically grouping Entities and VOs in this way, we provide a mechanism to strictly manage a grouping of objects, and a way to allow us to treat a number of different Entities and VOs as one.

Services

There can be no word more common in development, and no word used for such a multitude of different things as “service”

It was therefore unfortunate that Eric Evans introduced yet another concept of Service in DDD, one which has since been referred to by some as a Domain Service. However, people often use the term Service in isolation, as Eric did in his book – so if it is in relation to the Domain, it is probably what I prefer to call a Domain Service – otherwise it is likely to be one of those “other” services.

So … Domain Services Are … What?

Well, if Entities and Value Objects are the “things” in our Domain, the Services are a way of dealing with actions, operations and activities.

Shouldn’t Logic Be on the Entities Directly?

Yes, it really should. We should be modelling our Entities with the logic that relates to them and their children. But sometimes that logic either doesn’t fit on the Entity, or it would make the Entity bloated and unwieldy.

That is when Services come into the picture. They help us split logic that deals with multiple Entities, or that deals with complex operations or external responsibilities, into a separate structure more suited to the task.

So What Are the Characteristics of a Good Domain Service?

Oddly, they are much the same as the properties of any other good services… but to reiterate Eric Evans directly, they are:

  • The operation should be something that relates to a concept that does not naturally fit on an Entity or VO
  • The interface is defined in terms of the elements of the Domain Model
  • The operation is stateless

The first two of those are pretty obvious and easy to apply, the last is in general a good definition of a Service, and although some would argue there is no such thing as stateless, don’t express state explicitly, and presume instance of your Services should not affect each other.

Some Examples of Services

So far we have been using an ecommerce system as a basis for the examples, so I’ll continue here.

When a customer adds items to their shopping basket, they will expect to see the items totalled, and things like shipping costs calculated.

These things might conceivably sit on the Shopping Basket Entity – but a bit of deeper thought shows that this is probably not a perfect fit. For a simple system the basket might be able to contain all the logic required, but expanding our system and moving it to a DDD focus would probably extrapolate Services to deal with these issues – PricingService and ShippingCostingService could well be two of the players in this game.

After all, apart from needing to deal with the items in the Shopping Basket, these operations may well require communication with other services, calculation and pricing engines, external suppliers … in other words, they have a lot of responsibilities that we do not want to bring into our Entity.

Now instead of our ShoppingBasket having .GetTotalPrice and .GetShippingCost methods, we can use a Domain Service to get this information, and keep our Entity focused on the real tasks, managing the items in the ShoppingBasket

Services in the Ubiquitous Language

Entities and Value Objects live comfortably in the UL, they are the “things” that our Domain Experts and Users talk about.

Services also live in the UL, they are the actions that our DEs and Users talk about… where you see a noun in the conversation, it is likely to be an Entity or VO. When you see a verb, it may well be a Service

In Conclusion

Services provide a way of expressing actions and operations within our domain. Where as Entities and Value Objects, the “things”, provide the building blocks, Services provide the plumbing between them and allow us to express more abstract concepts.

Just beware of creating an anaemic domain model, Services are there to support Entities and VOs, not to strip their logic from them.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The generation of random numbers is too important to be left to chance.” - Robert R. Coveyou