Domain-Driven Design - Entities in C#
Before you look at the Entities example in C#, you can see at my post on what Entities are in Domain-Driven Design.
Entity interface in C#
public interface IEntity
{
Guid Id { get; }
}
About this code snippet:
- It have an
Id
property without aset
accessor, so this property could only be populated within the constructor of the class that implements it.
Entity base class in C#
public abstract class Entity : IEntity
{
public Guid Id { get; }
protected Entity() { }
protected Entity(Guid id)
{
this.Id = id;
}
public bool Equals(Entity other)
{
if (other is null) return false;
if (this.GetType() != other.GetType()) return false;
if (ReferenceEquals(this, other)) return true;
return this.Id.Equals(other.Id);
}
public override bool Equals(object obj) => this.Equals(obj as Entity);
public static bool operator ==(Entity a, Entity b) => a.Equals(b);
public static bool operator !=(Entity a, Entity b) => !a.Equals(b);
public override int GetHashCode()
{
return base.GetHashCode();
}
}
About this code snippet:
- It is an
abstract
class: This prevents this class from being instantiated and allows to declare abstract methods. - It have a
public
Id
property without aset
accessor, so this property can only be populated within the constructor. - It have an
Equals
method to verify equality with other entities by checking itsId
property.
Entity example in C#
public sealed class User : Entity
{
public FirstName FirstName { get; private set; }
private User() { }
private User(Guid id, FirstName firstName) : base(id)
{
this.FirstName = firstName;
}
public static User Create(Guid id, FirstName firstName)
{
return new User(id, firstName);
}
public void UpdateFirstName(FirstName firstName)
{
if (this.FirstName.Equals(firstName))
throw new InvalidFirstNameException();
this.FirstName = firstName;
}
}
About this code snippet:
- It is a
sealed
class: Prevent another class from extends from this one. - Their properties are
private set
, so they must be modified inside the same class instance. - No public
constructor
: Other classes cannot create instances of this class, except for nested classes. - To create instances of this Value Object we do not call the constructor directly, but it uses the Static Factory Method pattern instead.
- It have methods to change the value of the entity’s properties.
- If an attempt is made to update a property using the same value, it throws an exception.