Thomas Ploch
Principal Engineer @
It is difficult to guarantee the consistency of changes to objects in a model with complex associations. Invariants need to be maintained that apply to closely related groups of objects, not just discrete objects. Yet cautious locking schemes cause multiple users to interfere pointlessly with each other and make a system unusable.
Eric Evans; Domain-Driven Design: Tackling Complexity in the Heart of Software
Image Copyright: Stethoscope https://www.researchgate.net/figure/Large-graph-for-a-complex-SQL-query_fig4_235994954
Execution plan for a complex SQL query
Image Copyright: Stethoscope https://www.researchgate.net/figure/Large-graph-for-a-complex-SQL-query_fig4_235994954
Execution plan for a complex SQL query
We want successful systems to stay successful!
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.
Eric Evans; Domain-Driven Design: Tackling Complexity in the Heart of Software
The Aggregate boundary is a consistency boundary! That means that everything within this boundary should be immediately consistent whenever data changes occur. Preferably in an atomic operation, i.e. an ACID transaction.
The Aggregate boundary
All data changes are handled through the Aggregate ROOT, but the ROOT itself can delegate capabilities to other objects if needed.
The Aggregate boundary is a consistency boundary! That means that everything within this boundary should be immediately consistent whenever data changes occur. Preferably in an atomic operation, i.e. an ACID transaction.
Within the Aggregate Boundary Entities and Value Objects are allowed have references to each other, but they are only unique within the boundary.
References to Aggregate internals from the outer world are strictly forbidden!
Other parts of the model are allowed to hold references to the ROOT
Account
ENTITY
ROOT
Status
Value Object
Email
Value Object
Token
ENTITY
The Aggregate boundary
Outer
World
Invariant
Invariant
Invariants are rules that need to be satisfied at all times, they are an integral part of the consistency guarantees.
<?php
// Examples violating the principles
// We get an Account Aggregate somewhere
$account = $repository->get();
// Reference given to the outer world
$token = $account->getToken();
// Data changes not guarded by the root
$token->invalidate();
// Invariant checked outside the Aggregate
$challenge = Challenge::fromRequest($request);
if (!$account->getToken()->confirm($challenge)) {
throw new Exception('Challenge invalid.')
}
<?php
// A better approach
// We get an Account Aggregate somewhere
$account = $repository->get();
// Data changes handled through the root
$account->invalidateToken();
// Give out immutable values instead
$readOnlyToken = $account->getReadOnlyToken();
// Invariant checked within Aggregate
$challenge = Challenge::fromRequest($request);
$account->confirm($challenge);
// throws Domain Exception
Image attribution: Cyberguru, CC BY-SA 4.0, via Wikimedia Commons
Image attribution: Cyberguru, CC BY-SA 4.0, via Wikimedia Commons
Dániel Tarr & Bence-László Tarr
It's impossible to determine who is who just by the information we have here.
Account
ENTITY
ROOT
Status
Value Object
Email
Value Object
Token
ENTITY
ID
Value
Object
Account
ENTITY
ROOT
Status
Value Object
Email
Value Object
Token
ENTITY
Aggregates are distinct.
So they need to carry some kind of identity!
ID
Value
Object
...for the purpose of data changes.
Eric Evans; Domain-Driven Design: Tackling Complexity in the Heart of Software
Aggregates represent life-cycles
Account
ENTITY
ROOT
Status
Value Object
Token
ENTITY
Email
Value Object
During its lifetime the Aggregate processes relevant changes that make the model useful.
Account
ENTITY
ROOT
Genesis
Account
ENTITY
ROOT
The End
Time
...apply to closely related groups of objects, not just discrete objects.
Eric Evans; Domain-Driven Design: Tackling Complexity in the Heart of Software
Closely related things are normally also used together!
You probably keep your knifes, forks and spoons in the same drawer, right?
Database A
Database B
Account
ENTITY
Token
ENTITY
The Aggregate can only be reconstituted by querying all shards!
We have put our forks and spoons into different cupboards!
Account Microservice
Token Microservice
Account Microservice
Token Microservice
Now we have put our forks and spoons onto different PLANETS!
Database Shard A
Database Shard B
The cutlery is neatly organised in our drawers!
Customer Registration
Bounded Context
Account
ENTITY
ROOT
Status
Value Object
Email
Value Object
Token
ENTITY
Whenever a customer has opened an account, an entry in our CRM module should be created.
Account
ENTITY
ROOT
Contact
Value Object
Email
Value Object
CRM
Bounded Context
Any rule that spans Aggregates will not be expected to be up-to-date at all times. Through event processing, batch processing, or other update mechanisms, other dependencies can be resolved within some specified time.
Eric Evans; Domain-Driven Design: Tackling Complexity in the Heart of Software
As big as necessary
and as small as possible.
Someone somewhere I unfortunately can't remember. Please let me know if this quote is from you!
Presentation Slides @
Enforcing
invariants
Guaranteeing
consistency
Concurrent Access
Group of closely related domain objects