Entities and the aggregate rootThe aggregate owns its invariants
No narration yet
Module 4 · Lesson 213 min

The aggregate owns its invariants

The aggregate root is the entity that guards a cluster of objects and their rules. Outside code talks to the aggregate, never to its insides. Two conventions define how Fusion writes one, and they're the ones Nick's PR feedback hammered on.

First: action methods, not validate methods (convention N1, anti-pattern #5). The aggregate exposes verbs: ConfirmDecision, RevertDecision, CreateAndCalculate. Each validates internally and returns a Result<T>. There is no public Validate* a caller must remember to run first. The action IS the validation boundary.

An action method returns Result (FlightDisruptionAggregate.cs)
public Result<RevertDecisionResult> RevertDecision(string flightNumber, DateTime revokedAtUtc)
{
    if (!_revertSnapshotExists)
        return Result<RevertDecisionResult>.Failure(new EntityNotFound(nameof(flightNumber),
            $"No cost calculation snapshot exists for flight '{flightNumber}'. Calculate a cost before reverting a decision."));

    if (!_revertHasActiveDecision)
        return Result<RevertDecisionResult>.Failure(new DecisionNotFound(nameof(flightNumber),
            $"No active decision exists for flight '{flightNumber}'. Confirm a decision before attempting to revert."));

    return Result<RevertDecisionResult>.Success(new RevertDecisionResult(flightNumber, revokedAtUtc));
}

Second: the aggregate is loaded with the state it needs to enforce its rules (convention D1). The application layer is a thin orchestrator: it loads the aggregate whole, calls one action, and saves. It must not pre-compute domain state and pass booleans in, because then the aggregate is just a stateless validator over caller-supplied data and the real logic has leaked into the application layer.

Practice

Try it yourself

Do

Rename a validate into an action

Retrain the reflex from Nest's service.validateX() to an aggregate action.

Tick every step to confirm you did it.

Recall

What the application layer must not do

Convention D1 in one line.

Under convention D1, what is the application layer's job with an aggregate, and what must it NOT do?