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
	•	**contain presentation or formatting logic** (e.g., `formatForDisplay()`)

⸻

### Domain vs. Presentation Value Objects

It is critical to distinguish between **Domain Value Objects** and **Presentation Value Objects** (Displays):

- **Domain Value Objects (Core):** Protect business meaning and invariants. They are pure and know nothing about how data is shown to users.
- **Display Objects (Website):** Protect presentation logic and formatting. They are used by the Website (both server and client) to ensure consistent display.

If the API needs to return formatted strings, this logic belongs in **API Presenters**, not in Domain Value Objects.

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.