I\'m trying to get my head around how to properly use the repository pattern. The central concept of an Aggregate Root keeps coming up. When searching both the web and Stack
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.
Aggregate root is a complex name for simple idea.
Well designed class diagram encapsulates its internals. Point through which you access this structure is called aggregate root
.
Internals of your solution may be very complicated, but user of this hierarchy will just use root.doSomethingWhichHasBusinessMeaning()
.
Check this simple class hierarchy
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 main reason behind aggregate root
.
Aggregate root encapsulates multiple classes. you can manipulate whole hierarchy only through main object.
In Erlang there is no need to differentiate between aggregates, once the aggregate is composed by data structures inside the state, instead of OO composition. See an example: https://github.com/bryanhunter/cqrs-with-erlang/tree/ndc-london
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.
Imagine you have a Computer entity, this entity also cannot live without its Software entity and Hardware entity. These form the Computer
aggregate, the mini-ecosystem for the Computer portion of the domain.
Aggregate Root is the mothership entity inside the aggregate (in our case Computer
), it is a common practice to have your repository only work with the entities that are Aggregate Roots, and this entity is responsible for initializing the other entities.
Consider Aggregate Root as an Entry-Point to an Aggregate.
In C# code:
public class Computer : IEntity, IAggregateRoot
{
public Hardware Hardware { get; set; }
public Software Software { get; set; }
}
public class Hardware : IEntity { }
public class Software : IValueObject { }
public class Repository<T> : IRepository<T> where T : IAggregateRoot {}
Keep in mind that Hardware would likely be a ValueObject too (do not have identity on its own), consider it as an example only.