Domain Objects Design Guide (Clean Architecture)

This document defines all domain object types used in the Core and assigns strict responsibilities and boundaries.

Its goal is to remove ambiguity between:
	•	Entities
	•	Value Objects
	•	Aggregate Roots
	•	Domain Services
	•	Domain Events

The rules in this document are non-negotiable.

⸻

Core Principle

Domain objects represent business truth.

They:
	•	outlive APIs and UIs
	•	must remain stable over time
	•	must not depend on technical details

If a class answers a business question, it belongs here.

⸻

1. Entities

Definition

An Entity is a domain object that:
	•	has a stable identity
	•	changes over time
	•	represents a business concept

Identity matters more than attributes.

⸻

Responsibilities

Entities MUST:
	•	own their identity
	•	enforce invariants on state changes
	•	expose behavior, not setters

Entities MUST NOT:
	•	depend on DTOs or transport models
	•	access repositories or services
	•	perform IO
	•	know about frameworks

⸻

Creation Rules
	•	New entities are created via create()
	•	Existing entities are reconstructed via rehydrate()

core/<context>/domain/entities/


⸻

Example
	•	League
	•	Season
	•	Race
	•	Driver

⸻

2. Value Objects

Definition

A Value Object is a domain object that:
	•	has no identity
	•	is immutable
	•	is defined by its value

⸻

Responsibilities

Value Objects MUST:
	•	validate their own invariants
	•	be immutable
	•	be comparable by value

Value Objects MUST NOT:
	•	contain business workflows
	•	reference entities
	•	perform IO

⸻

Creation Rules
	•	create() for new domain meaning
	•	fromX() for interpreting external formats

core/<context>/domain/value-objects/


⸻

Example
	•	Money
	•	LeagueName
	•	RaceTimeOfDay
	•	SeasonSchedule

⸻

3. Aggregate Roots

Definition

An Aggregate Root is an entity that:
	•	acts as the consistency boundary
	•	protects invariants across related entities

All access to the aggregate happens through the root.

⸻

Responsibilities

Aggregate Roots MUST:
	•	enforce consistency rules
	•	control modifications of child entities

Aggregate Roots MUST NOT:
	•	expose internal collections directly
	•	allow partial updates bypassing rules

⸻

Example
	•	League (root)
	•	Season (root)

⸻

4. Domain Services

Definition

A Domain Service encapsulates domain logic that:
	•	does not naturally belong to a single entity
	•	involves multiple domain objects

⸻

Responsibilities

Domain Services MAY:
	•	coordinate entities
	•	calculate derived domain values

Domain Services MUST:
	•	operate only on domain types
	•	remain stateless

Domain Services MUST NOT:
	•	access repositories
	•	orchestrate use cases
	•	perform IO

core/<context>/domain/services/


⸻

Example
	•	SeasonConfigurationFactory
	•	ChampionshipAggregator
	•	StrengthOfFieldCalculator

⸻

5. Domain Events

Definition

A Domain Event represents something that:
	•	has already happened
	•	is important to the business

⸻

Responsibilities

Domain Events MUST:
	•	be immutable
	•	carry minimal information

Domain Events MUST NOT:
	•	contain behavior
	•	perform side effects

core/<context>/domain/events/


⸻

Example
	•	RaceCompleted
	•	SeasonActivated

⸻

6. What Does NOT Belong in Domain Objects

❌ DTOs
❌ API Models
❌ View Models
❌ Repositories
❌ Framework Types
❌ Logging
❌ Configuration

If it depends on infrastructure, it does not belong here.

⸻

7. Dependency Rules

Entities → Value Objects
Entities → Domain Services
Domain Services → Entities

Reverse dependencies are forbidden.

⸻

8. Testing Requirements

Domain Objects MUST:
	•	have unit tests for invariants
	•	be tested without mocks

Domain Services MUST:
	•	have deterministic tests

⸻

Mental Model (Final)

Entities protect state.
Value Objects protect meaning.
Aggregate Roots protect consistency.
Domain Services protect cross-entity rules.
Domain Events describe facts.

⸻

Final Summary
	•	Domain objects represent business truth
	•	They are pure and framework-free
	•	They form the most stable part of the system

If domain objects are clean, everything else becomes easier.