Table of Contents

Interface IAggregate

Namespace
Trellis
Assembly
Trellis.DomainDrivenDesign.dll

Defines the contract for an aggregate root in Domain-Driven Design. An aggregate is a cluster of domain objects that can be treated as a single unit for data changes.

public interface IAggregate : IChangeTracking
Inherited Members
Extension Methods

Examples

public class Order : Aggregate<OrderId>
{
    private readonly List<OrderLine> _lines = [];

    public Result<Order> AddLine(ProductId productId, int quantity)
    {
        // Business logic here
        DomainEvents.Add(new OrderLineAddedEvent(Id, productId, quantity));
        return this;
    }
}

Remarks

Aggregates are the primary building blocks of domain models in DDD. Key characteristics:

  • Aggregate roots are the only entry point for modifications to the aggregate
  • Ensures all business rules and invariants within the aggregate boundary are maintained
  • Tracks domain events that occur during state changes
  • External objects can only hold references to the aggregate root, not internal entities

This interface combines change tracking (from IChangeTracking) with domain event management.

Properties

ETag

Gets the entity tag (ETag) for optimistic concurrency per RFC 9110.

string ETag { get; }

Property Value

string

An opaque string token that changes each time the aggregate is persisted. Used by the persistence layer to detect concurrent modifications and by the HTTP layer for conditional requests (ETag/If-Match headers).

Remarks

The ETag is managed automatically by the persistence infrastructure:

  • For SQL databases, the EF Core interceptor generates a new GUID on each save
  • For CosmosDB, the native _etag is used directly

Domain code should not modify this property directly.

Methods

UncommittedEvents()

Gets all domain events that have been raised but not yet marked as committed.

IReadOnlyList<IDomainEvent> UncommittedEvents()

Returns

IReadOnlyList<IDomainEvent>

A read-only list of domain events that occurred during state changes since the last call to AcceptChanges(). Returns an empty list if no uncommitted events exist.

Remarks

Domain events represent significant state changes that have occurred within the aggregate. These events should be:

  • Published to an event bus or message broker after successful persistence
  • Used to trigger side effects or update read models
  • Cleared by calling AcceptChanges() after successful processing

Typical workflow:

// 1. Execute domain operation
var result = order.Submit();

// 2. Save aggregate to repository
await repository.SaveAsync(order);

// 3. Publish uncommitted events
foreach (var evt in order.UncommittedEvents())
{
    await eventBus.PublishAsync(evt);
}

// 4. Mark changes as committed
order.AcceptChanges();