Playbook
DDD in Code: Entity, Value Object, and Aggregate (Ddd In Code Entity Value Object And Aggregate)
How do DDD tactical patterns work? Explore Value Objects, Entities, Aggregates, Domain Services, Application Services, and Repository boundaries through a…
The question of this chapter
The first chapter began with business language and rules. Now we ask: where do those decisions live in code? We will keep the same e-commerce domain: money, order lines, orders, and payment flow.
Money + Address + Quantity → Value Object
OrderLine + Order → Entity
Order (Root) + OrderLine → Aggregate
Repository → loads and saves the Aggregate Root
Concepts at first use
📦 Value Object
A concept without identity, defined by its value and kept immutable.
📦 Entity
An object that remains the same through its identity even when attributes change.
📦 Aggregate
The transaction boundary for objects that must remain consistent together.
📦 Aggregate Root
The only external entry point into an Aggregate.
Two Money(100, "TRY") values represent the same value; two orders are different even if their totals match.
Start with Value Objects
Value Objects best teach the DDD mindset. A decimal is not money by itself: currency, rounding, discount, and tax rules belong to the concept. Keep those rules with the concept rather than scattering them through services.
public sealed record Money(decimal Amount, string Currency)
{
public Money Add(Money other)
{
if (Currency != other.Currency) throw new DomainException("Currency mismatch");
return new Money(Amount + other.Amount, Currency);
}
}
Value Objects are immutable. A changed value becomes a new instance, preventing side effects across pricing, delivery, and payment steps. Equality is structural: it costs O(m) for m fields, but a rule kept in one place reduces future change cost.
Entity: identity and lifecycle
Order is an Entity. Its address or total may change, yet it remains the same order. The important distinction is not avoiding setters; it is modelling a transition as business behaviour.
❌ order.Status = Paid
✓ order.ConfirmPayment(payment)
❌ order.Total = total - discount
✓ order.ApplyDiscount(discount)
ConfirmPayment validates payment evidence, cancellation state, and the legal transition in one place. An Entity should protect invalid states in its own lifecycle.
Aggregate: not an object group, a consistency boundary
Order and its lines must remain consistent in one transaction: quantity is positive, total matches lines, and lines may become immutable after confirmation. Therefore Order is the Aggregate Root for OrderLine.
Order aggregate
├─ OrderLine
├─ ShippingAddress
└─ Total
External code → Order.AddLine() / Order.ConfirmPayment()
External code ↛ writes to OrderLine directly
A large Aggregate does not create safety; it creates locks and version conflicts. Update one Aggregate per write whenever possible. Reference other Aggregates by ID, then coordinate through application orchestration or domain events.
Domain Service, Application Service, and Repository
A Domain Service models pure domain behaviour that fits no single Aggregate, such as exchange-rate pricing. An Application Service orchestrates the use case: load an order, call behaviour, save it, and complete a transaction.
| Layer | Responsibility |
|---|---|
| Value Object / Entity / Aggregate | Protect business rules and invariants |
| Domain Service | Pure domain calculation spanning no single Aggregate |
| Application Service | Use-case orchestration, transactions, adapters |
A Repository is not a table API. It provides collection-like persistence only for Aggregate Roots. Loading an OrderLine directly bypasses the Root and its rules.
order = orderRepository.get(orderId)
order.confirmPayment(payment)
orderRepository.save(order)
This boundary also makes domain behaviour testable with a fake repository without shaping the model around an ORM.
Associations that create false confidence
❌ Value Object = a small DTO
✓ It carries value, validation, and behaviour.
❌ Aggregate = the largest possible object graph
✓ It is a deliberately small transaction and consistency boundary.
❌ Domain Service = a dumping ground for rules
✓ It is pure domain behaviour belonging to no single Aggregate.
❌ Repository = CRUD API for every table
✓ It is the persistence boundary for an Aggregate Root.
Implementation checklist
- Are
Money,Email,Address, andQuantitytravelling as primitives? - Does every Entity behaviour prevent a real invalid state?
- Does one write attempt to update multiple Aggregates atomically?
- Does the Application Service decide rules or merely orchestrate them?
- Do repositories load Aggregate Roots only?
Start with the most expensive business rule; do not convert the whole system to tactical DDD at once.
What should remain with you
A Value Object carries a concept's value and rules. An Entity protects identity and lifecycle. An Aggregate draws a small transaction boundary for consistency. A Repository connects that boundary to persistence.
Tactical DDD is not about more classes. It prevents business rules from leaking into the wrong layer.
The next chapter moves these boundaries into a large system: Bounded Contexts, Context Mapping, and separation inside a monolith.
FAQ
Frequently asked questions
What is Value Object?
A concept without identity, defined by its value and kept immutable.
What is Entity?
An object that remains the same through its identity even when attributes change.
Is it true that "Value Object = a small DTO"?
It carries value, validation, and behaviour.
What does this part lock in?
The first chapter began with business language and rules. Now we ask: where do those decisions live in code? We will keep the same e-commerce domain: money, order lines, orders, and payment flow. The first chapter began with business language and rules. Now we ask: where do those decisions live in code? We will keep the same e-commerce domain: money, order lines, orders, and payment flow.
Engineering Principles Learned
- Value Objects combine primitive data with business meaning and validation.
- Aggregates should be small, explicit consistency boundaries.
- Application Services orchestrate; domain behaviour decides.
Continue reading
Continue reading
Related articles
DDD- Designing Software Around the Business, Not the Database
What is Domain-Driven Design? A guide to the limits of data-driven design, the power of ubiquitous language, and when DDD is a worthwhile investment.
Related articles
DDD in Production: Distributed Systems and Modernization Strategies
How does DDD survive production change? A guide to Event Storming, Saga, Transactional Outbox, Anti-Corruption Layers, and Strangler Fig modernization.
Related articles
How Does DDD Work in Large Systems?
How does DDD scale in large systems? A decision guide to Bounded Contexts, Context Mapping, Conway's Law, modular monoliths, microservice boundaries, and…