When I was first getting my head around Domain Driven Design (DDD), I had a hard time reconciling Entities with the Single Responsibility Principle. By their very nature, some entities can have an awful lot of state hanging off them.
Take for example our UserAccount entity at Pluralsight. There’s a lot of stuff associated with a user account:
- The Id for the account, making it unique
- Profile information that allows the user to customize her experience (name, email, etc.)
- A password verifier that allows us to authenticate the user
- Statistics about the account – when was it created? When was the last time the user logged in?
- Preferences for the user – does she prefer our Silverlight or our WM player?
- Subscription for the user – does she have a Plus subscription? A trial? etc.
- and the list goes on…
Looking at all of this, it sure makes it hard to see what the single responsibility of the UserAccount should be.
I remember struggling with this and going back to the Evans book for guidance. Fortunately he didn’t let me down – it’s all in there as clear as day. It just takes a knucklehead like me a couple of reads to really get it:
When an object is distinguished by its identity, rather than its attributes, make this primary to its definition in the model. Keep the class definition simple and focused on life cycle continuity and identity.
There’s more to it than this, and I encourage you to study part 2 of the DDD book to derive your own opinion, but one thing has been crystallizing for me:
The single responsibility of an Entity is to identify itself uniquely in a sea of other Entities.
In other words, I should be able to find the Entity by its ID, or possibly by other searchable attributes. Each UserAccount entity has a unique Id, and I can use the corresponding repository to find a particular UserAccount from among the thousands of accounts at Pluralsight by simply supplying an Id. But I can also supply an Email address and find the account that way. I can search for all UserAccount objects that have a last name of “Simpson”, etc.
What’s UserAccount’s single responsibility? Being findable.
So what about all of that other state that’s hanging off of the UserAccount? What about the Preferences? The Subscription? There’s a lot there that has nothing at all to do with the lifecycle or “findability” of the UserAccount. Doesn’t that violate SRP?
It turns out that if you adhere to the guidelines of DDD, you’ll end up packaging all of that other state into separate classes (Value Objects) that break it down into smaller, more manageable chunks. The Entity then delegates responsibility to these classes, keeping the Entity class itself simple and focused on findability.
For example, when you look at our UserAccount class, you’ll see a property of type UserPreferences hanging off of it. UserPreferences is a class representing a Value Object that tells us which player the user prefers, whether she likes to see the course catalog expanded or collapsed, and so on. By the simple act of pushing that responsibility down into the UserPreferences class, the UserAccount class no longer has responsibility for worrying about that stuff. The UserAccount simply needs a getter and a setter so the UserPreferences can be updated, but it doesn’t need to care about the business rules of getting and setting individual preferences. That’s the responsibility of the UserPreferences class.
Following this guidance, our UserAccount object has become surprisingly simple.
What’s your experience with Entities been like? Comment on this post to share your challenges!