TaskHub.Shared

Domain Deep Dive

TaskHub.Shared.Domain provides the core building blocks for implementing a rich domain model using Domain-Driven Design (DDD).

Aggregates & Aggregate Roots

An Aggregate is a cluster of domain objects that can be treated as a single unit. An Aggregate Root is the entry point to the aggregate.

AggregateBase

All aggregate roots should inherit from this class. It provides:

public class TaskItem : AggregateBase<Guid>
{
    public TaskItem(Guid id, string title) : base(id)
    {
        Title = title;
        AddEvent(new TaskCreatedDomainEvent(id, title));
    }

    public string Title { get; private set; }
    public TaskStatus Status { get; private set; } = TaskStatus.Pending;

    public void Complete()
    {
        if (Status == TaskStatus.Completed) return;
        Status = TaskStatus.Completed;
        AddEvent(new TaskCompletedDomainEvent(Id));
    }
}

Domain Events

Domain events represent a significant occurrence within the domain. They are handled within the same transaction as the aggregate change (unless using the Outbox pattern for integration).

Defining an Event

Events should be immutable and implement IDomainEvent.

public record TaskCompletedDomainEvent(Guid TaskId) : DomainEventBase;

Handling an Event

Implement IEventHandler<T> to react to events.

public class NotifyOwnerOnTaskCompleted(IEmailService emailService) : IEventHandler<TaskCompletedDomainEvent>
{
    public async Task HandleAsync(TaskCompletedDomainEvent ev, CancellationToken ct)
    {
        // ... send email logic
    }
}

Domain Exceptions

Use domain-specific exceptions to capture business rule violations. This allows the application layer to distinguish between technical errors and business logic failures.

public class TaskAlreadyCompletedException() : DomainException("Task is already completed.");

Best Practices

  1. Encapsulation: State changes should only happen through public methods on the aggregate root. Properties should have private set.
  2. Invariants: The aggregate root is responsible for ensuring that all business rules (invariants) are satisfied before applying a change.
  3. Small Aggregates: Prefer smaller aggregates to reduce contention and improve performance. Reference other aggregates by ID only.