One concept in Domain Driven Design that may be familiar to you is entities. Most object relational mappers like Entity Framework and NHibernate use the term “entity”. In their context an entity is an object that maps a row of data to a table. Although ORMs and DDD share the same term and they’re similar, there are additional rules at play with entities in Domain Driven Design.
Pro tip: Entities represent long lived nouns in your business’s domain. Some examples of entities that may be in a university domain are Professor, Student and Course.
Let’s jump right into what makes a domain entity.
Entities have never changing identity
Entities have identity because they need to be fetched from the database and referenced during update and delete operations. Many times entities mutate over time, however their identity should remain constant across these mutations. If you change your name does that make you a new person? Of course not, you’re still the same person you were prior changing your name.
Regarding how and where to generate entity identifiers… I always advise to use application generated IDs instead of database ones! A qualifying identifier type for this is GUID. In some cases you can use a composite primary key Value Object.
Unlike Value Objects, entities always have identity, be sure to read about Value Objects.
They are mutable
Think of a user in your domain. After user registration you probably want to allow them to change their email and name because those are very real traits of a user that can change from time to time. Therefore in this scenario the User
entity would allow email and name to mutate over time.
While the bulk of entities in your domain are going to be mutable, some may be immutable. For example, your business may call for a write-once read-only audit log entity. It’s a bad idea for an audit log to change after creation from an audit and security perspective. For entities that should be immutable after creation I recommend only allowing their properties to be set in the object constructor.
Pro tip: Mutability is one of the important differences between a Domain Entity and Value Object.
Encapsulates business invariants, private setter properties
Probably the most notable rule that Domain Driven Design highly encourages on entities is business invariant encapsulation. This is an art of protecting all your business invariants through the use of encapsulation by putting them behind methods. The only way entity consumers can execute this business logic is by the calling the methods.
I love this pattern and highly encourage it because it allows you to neatly tuck business logic away and consumers never need to worry about them. What goes hand-in-hand with this is the concept of read-only entity properties. Of course to truly protect your business invariants that revolve around an entity it means you have to forbid property modifications from consumers. If consumers need to modify a property they go through an entity method that has the necessary business invariants.
Favor Value Objects as properties over entities
Ah, Value Objects. These are one of my favorite concepts in Domain Driven Design. They make it easier to pass around bits of integral units that are always valid. Many times than not you’ll find that most of your entity properties can be implemented as Value Objects instead of basic primitives or child entities. In fact, I highly encourage this! I completely agree doing this results in more classes, but I feel like it’s worth it.
In my opinion they improve code readability at the sacrifice of extra key strokes. If you haven’t already read through my Value Object Course to fully understand the benefits they bring to the table as well as some down sides. I already wrote about Value Objects as Entity Properties so I’ll spare the duplication.
Avoid dependency injection
The final Domain Driven Design entity rule I want to talk about is Dependency Injection. You should never use dependency injection to inject any object instance/service into your entity. Doing this is a sign of code smell and if you do this it means your entity is coupled too tightly to a service. When you find yourself injecting a service into an Entity you need to extract out and into a Domain Service.
For example, let’s say you have a User
entity and need to allow user’s to change their password. Well, this feature will need to do a couple of things.
- Accept the user’s clear text password string.
- Encrypt the clear text password and set the entity’s password hash property.
- Save the User entity mutation to the database.
Here’s a tight coupling example.
public namespace Domain.User
{
public class User : IAggregate
{
public PersonName Name { private set; get; }
public string PasswordHash { private set; get; }
public User(PersonName name, string passwordHash)
{
Name = name;
PasswordHash = passwordHash;
}
/// BAD! Now the entity has to have knowledge about a service
public void ChangePassword(IPasswordEncrypter encrypter, string clearTextPassword)
{
PasswordHash = encrypter.SecureEncrypt(clearTextPassword);
}
}
}
IPasswordEncrypter
in the User
entity is code smell! In fact it’s clear we missed an opportunity for a domain service. Here’s an arguably better way to implement this.
public namespace Domain.User
{
public class User : IAggregate
{
public PersonName Name { private set; get; }
public string PasswordHash { private set; get; }
public User(PersonName name, string passwordHash)
{
Name = name;
PasswordHash = passwordHash;
}
/// Why is the method internal? This is my way of forcing consumers to go
/// through ChangeUserPasswordService. Consumers outside the domain layer cannot
/// call into User.ChangePassword(string) directly now. Which in my humble opinion is good.
internal void ChangePassword(string passwordHash)
{
PasswordHash = passwordHash;
}
}
public class ChangeUserPasswordService
{
private IPasswordEncrypter _encrypter;
private IUserRepository _userRepository;
public ChangeUserPasswordService(IPasswordEncrypter encrypter, IUserRepository userRepository)
{
_encrypter = encrypter;
_userRepository = userRepository;
}
public void ChangePassword(Guid userId, string clearTextPassword)
{
var user = _userRepository.Get(userId);
if (user == null) {
// throw exception or return a Value Object result object
}
user.ChangePassword(_encrypter.SecureEncrypt(clearTextPassword));
}
}
}
What if the user forgets their password? Let’s see the changes we can make to allow the system to change the user’s password.
public namespace Domain.User
{
public class ForgotPasswordService
{
private IPasswordGenerator _generator;
private ChangeUserPasswordService _changeService;
/// Since ChangeUserPasswordService already takes care of changing a user's password
/// hash with the hash of a provided clear text password we don't rebuild that logic. Instead
/// we create this domain service that's injected with ChangeUserPasswordService leaving
/// this domain service the only responsibility of generating a random password
/// string and facilitating the call into ChangeUserPasswordService.
public ChangeUserPasswordService(
IPasswordGenerator generator,
ChangeUserPasswordService changeService
)
{
_generator = generator;
_changeService = changeService;
}
public void ResetPassword(Guid userId)
{
var secureRandomPassword = _generator.NewRandomPassword();
_changeService.ChangePassword(userId, secureRandomPassword);
}
}
}
I hope this gives you a good idea as to why you should avoid dependency injecting services into entities. If you embed too much business logic in entities that require services this results in an overly complicated and fragile entity.
Summary
Well that about wraps this up. To summarize the rules of entities:
- They are long-lived mutable nouns in the domain.
- Entities have never changing identity, unlike Value Objects.
- Don’t inject services and aggregates into entities.
- Favor Value Object properties over child entities when ever possible.