Entity Best Practices & Conventions
Entities
Every aggregate root is also an entity. So, these rules are valid for aggregate roots too unless aggregate root rules override them.
- Do define entities in the domain layer.
Primary Constructor
- Do define a primary constructor that ensures the validity of the entity on creation. Primary constructors are used to create a new instance of the entity by the application code.
- Do define primary constructor as
public
,internal
orprotected internal
based on the requirements. If it's not public, the entity is expected to be created by a domain service. - Do always initialize sub collections in the primary constructor.
- Do not generate
Guid
keys inside the constructor. Get it as a parameter, so the calling code will useIGuidGenerator
to generate a newGuid
value.
Parameterless Constructor
- Do always define a
protected
parameterless constructor to be compatible with ORMs.
References
- Do always reference to other aggregate roots by Id. Never add navigation properties to other aggregate roots.
Other Class Members
- Do always define properties and methods as
virtual
(exceptprivate
methods, obviously). Because some ORMs and dynamic proxy tools require it. - Do keep the entity as always valid and consistent within its own boundary.
- Do define properties with
private
,protected
,internal
orprotected internal
setter where it is needed to protect the entity consistency and validity. - Do define
public
,internal
orprotected internal
(virtual) methods to change the properties (with non-public setters) if necessary.
- Do define properties with
Aggregate Roots
Primary Keys
- Do always use a Id property for the aggregate root key.
- Do not use composite keys for aggregate roots.
- Do use Guid as the primary key of all aggregate roots.
Base Class
- Do inherit from the
AggregateRoot<TKey>
or one of the audited classes (CreationAuditedAggregateRoot<TKey>
,AuditedAggregateRoot<TKey>
orFullAuditedAggregateRoot<TKey>
) based on requirements.
Aggregate Boundary
- Do keep aggregates as small as possible. Most of the aggregates will only have primitive properties and will not have sub collections. Consider these as design decisions:
- Performance & memory cost of loading & saving aggregates (keep in mind that an aggregate is normally loaded & saved as a single unit). Larger aggregates will consume more CPU & memory.
- Consistency & validity boundary.
Example
Aggregate Root
The Entity
References
- Effective Aggregate Design by Vaughn Vernon http://dddcommunity.org/library/vernon_2011