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…

DDD tactical patterns showing value objects, entities, and aggregate boundaries

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

  1. Are Money, Email, Address, and Quantity travelling as primitives?
  2. Does every Entity behaviour prevent a real invalid state?
  3. Does one write attempt to update multiple Aggregates atomically?
  4. Does the Application Service decide rules or merely orchestrate them?
  5. 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

Related articles

Related articles

ESSAY

How does DDD scale in large systems? A decision guide to Bounded Contexts, Context Mapping, Conway's Law, modular monoliths, microservice boundaries, and…

Paylaş