Skip to content

ADR-008: DDD Base Classes

Status: Accepted Date: 2026-02-07

Context

The domain model has entities with varying levels of importance and lifecycle ownership. Some entities are independent (Project, Character), while others only exist within the context of a parent (Section within TimelineUpdate, FactionMembership within Faction). The team follows Domain-Driven Design patterns.

Decision

Use DDD base classes: AggregateRoot, Entity, and Value Objects.

Entity Hierarchy

Entity (base class)

All domain objects with identity inherit from Entity.

FieldTypeNotes
IdGuid (UUIDv7)Internal only
CreatedAtDateTimeAuto-set via DbContext SaveChanges override
CreatedByGuidAuto-set from current user context
UpdatedAtDateTimeAuto-set on insert and update
UpdatedByGuidAuto-set from current user context

AggregateRoot : Entity

Top-level domain objects that define transactional boundaries.

FieldTypeNotes
SequenceNumberintProject-scoped public ID

Additionally supports domain events collection for raising events on state changes.

Value Objects

Objects with no identity, compared by their properties. Immutable.

Classification

TypeEntities
Aggregate RootsProject, Character, Faction, TimelineUpdate
EntitiesSection, Item, ProjectMember, FactionMembership, Relationship, FactionRelationship, ChangelogEntry
Value ObjectsTheme (primaryColor + accentColor), RelationshipType, DevelopmentStatus

Rationale

  • Aggregate roots define consistency boundaries - all changes to child entities go through the aggregate root
  • Only aggregate roots get public sequence numbers - child entities are accessed through their parent and don't need public-facing identifiers
  • Audit fields on all entities via the base Entity class, auto-populated by the DbContext so developers never need to set them manually
  • Protected setters enforce that state changes go through domain methods, not direct property assignment

Consequences

  • EF Core entity configurations need to handle the inheritance hierarchy
  • SaveChangesAsync override in DbContext auto-populates CreatedAt/CreatedBy/UpdatedAt/UpdatedBy
  • Domain events on AggregateRoot can trigger side effects (activity feed entries, notifications) through MediatR or similar
  • Value Objects configured as owned types in EF Core