What's an Aggregate Root?

In the context of the repository pattern, aggregate roots are the only objects your client code loads from the repository.

The repository encapsulates access to child objects - from a caller's perspective it automatically loads them, either at the same time the root is loaded or when they're actually needed (as with lazy loading).

For example, you might have an Order object which encapsulates operations on multiple LineItem objects. Your client code would never load the LineItem objects directly, just the Order that contains them, which would be the aggregate root for that part of your domain.


From Evans DDD:

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single, specific ENTITY contained in the AGGREGATE.

And:

The root is the only member of the AGGREGATE that outside objects are allowed to hold references to[.]

This means that aggregate roots are the only objects that can be loaded from a repository.

An example is a model containing a Customer entity and an Address entity. We would never access an Address entity directly from the model as it does not make sense without the context of an associated Customer. So we could say that Customer and Address together form an aggregate and that Customer is an aggregate root.


The aggregate root is a complex name for a simple idea.


General idea

Well designed class diagram encapsulates its internals. Point through which you access this structure is called aggregate root.

enter image description here

Internals of your solution may be very complicated, but users of this hierarchy will just use root.doSomethingWhichHasBusinessMeaning().


Example

Check this simple class hierarchy enter image description here

How do you want to ride your car? Chose better API

Option A (it just somehow works):

car.ride();

Option B (user has access to class inernals):

if(car.getTires().getUsageLevel()< Car.ACCEPTABLE_TIRE_USAGE)
    for (Wheel w: car:getWheels()){
        w.spin();
    }
}

If you think that option A is better then congratulations. You get the main reason behind aggregate root.


Aggregate root encapsulates multiple classes. you can manipulate the whole hierarchy only through the main object.