DDD Domain Driven Design Aggregates
When learning about Domain Driven Design one of the harder things is wrapping your head around what aggregates are. In this post I list some facts, some may be redundant, but redundancy helps reinforce concepts.
Here is my personal list of facts and notes about domain driven design aggregates in no general order. This list will be updated as time goes on.
List of Aggregate facts
- An aggregate is a consistency boundary that decomposes large models into smaller clusters of domain objects that are technically easier to manage. Basically I refer to these as modules/services, modules should be easily hot swappable.
- An Aggregate is a DDD pattern, a cluster of related domain objects that work together as a single unit
- Aggregates will have one component object that acts as an interface to the aggregate known as the Aggregate root All outside interaction with the aggregate should be through the aggregate root. An aggregate root is the contract for the entire aggregate, the API interface for the Aggregate/Module/Service
- Aggregates are the inner bounded contexts that build the complete bounded context of the entire domain, like building blocks.
- An aggregate is a way to group objects inside of a specific domain bounded context. They are a way to modularize things that have to work together to perform a specific task of the system
- Aggregates are collections of objects that perform the functions of a particular bounded context.
- Aggregates are stored in a repository, aka database
- Aggregates are the only thing that can be persisted and retrieved ( re-hydrated )
- Repositories are used to manage the persistence of aggregates and ensure a clear separation between the data and the domain
- Aggregates communicate with other aggregates via Events, creating event driven systems
- Try to make aggregates as small and specific as possible, this creates less room for bugs and makes the system easier to maintain. Stick to the Single Responsibility Principle
- Large aggregates can suffer from performance issues when they span multiple entities, data stores, database tables Only make aggregates as large as they absolutely have to be, eliminate all unrelated code and actions.
- An Aggregate lives in a Bounded Context. Each bounded context is made up of multiple aggregates. Each a aggregate within a bounded context should have a Single Responsibility.
- When two or more objects/entities need to interact with each other to perform a task, make them part of the same aggregate.
- Aggregates represent concepts in the larger domain.
- Aggregates should be behavior focused
- Each object within an aggregate should be required to do some action for the aggregate to be included. Don’t have redundant aggregates within other bounded contexts, this is a clue that a new aggregate needs to be born into its own bounded context. Create another module and make the redundant aggregate it’s own.
- An aggregate root should be the only entry point for an aggregate. The aggregate root is like an application programmers interface to the aggregate API.
- The aggregate root is the coordinator for the aggregate. It handles all of the events by calling the aggregate objects to perform the required tasks/actions.
- The aggregate root should expose only the behaviors required by other aggregates. The aggregate root is the contract of the aggregate. Removing behaviors makes breaking changes and can destroy a system. However, adding new behaviors any time is safe.
- Objects within aggregates should not hold references to objects in other aggregates, this creates tight coupling, exactly what we are trying to prevent. This means no object in one module/service/microservice should directly call an object or refer to one inside another module/service/microservice. This means no communication outside one bounded context to another, the aggregate root is the only way to communicate.
- An aggregate can span several database tables when persisted, but this can cause problems.
- Domain objects within an aggregate can have direct object references to each other. This means that aggregates ( objects ) within a single bounded context (module/service) can have references to each other. Meaning within a service you can call other objects within the service.
- Repositories should hold aggregates. Aggregates can be reconstructed at any time from a repository.
- DDD repositories are not the same as code repositories like github
- The aggregate roots are AKKA actors that should accept commands and produce events
- Repositories are used to manage domain object/aggregate root persistence
- A repository manages the retrieval of aggregate/domain objects while ensuring a separation between the domain and data models
- A repository is a pattern for storing and retrieving the pieces of an aggregate in a database
- A repository enforces the aggregate roots contract by providing the interface to store and retrieve the aggregate parts
- Repositories map to the aggregates not the actual data-store such as database tables
Developing microservices with aggregates – Chris Richardson
I found this to be one of the best explanations of aggregates and how they relate in microservice based systems. Here is a link to the site mentioned in the video microservices.io where you can find a lot more useful information about microservices.