0% found this document useful (0 votes)
19 views45 pages

Tactical Design With Aggregaates

Chapter 5 discusses Tactical Domain-Driven Design (DDD), focusing on design patterns and building blocks for creating domain-driven systems, including the concepts of entities, value objects, and aggregates. It emphasizes the importance of protecting business invariants, designing small aggregates, and using domain events for system communication. The chapter also contrasts DDD with CRUD applications, highlighting the benefits of a well-structured domain model in maintaining business processes and data integrity.

Uploaded by

tashkovaani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views45 pages

Tactical Design With Aggregaates

Chapter 5 discusses Tactical Domain-Driven Design (DDD), focusing on design patterns and building blocks for creating domain-driven systems, including the concepts of entities, value objects, and aggregates. It emphasizes the importance of protecting business invariants, designing small aggregates, and using domain events for system communication. The chapter also contrasts DDD with CRUD applications, highlighting the benefits of a well-structured domain model in maintaining business processes and data integrity.

Uploaded by

tashkovaani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

Tactical Design with

Aggregates
CHAPTER 5
Introduction
▪ Tactical DDD is a set of design patterns and building blocks that you
can use to design domain-driven systems.
▪ Even for projects that are not domain-driven, you can benefit from
using some of the tactical DDD patterns.
▪ Tactical design deals with classes and modules.
▪ The purpose of tactical design is to refine the domain model to a
stage where it can be converted into working code.
Recap…
Context Mapping

Core Domain Supporting Subdomain


Domain-Driven
Design and
CRUD
A CRUD application is only about
structuring, displaying and editing
data.

It normally does not support the


underlying business process.

In a CRUD system, the reason for a


change is lost and the business
process is in the head of the user.
DDD vs CRUD
Company A: Company B:
The manager looks up the employee’s record in the The manager looks up the employee’s record in the system.
system.
The manager puts a check in the 'Contract terminated'
The manager selects the 'Terminate Employment checkbox and enters the termination date, then clicks 'Save'.
Contract' action.
The manager logs into the user management system, looks
The system asks for the termination date and up the user’s account, puts a check in the 'Disabled'
reason. checkbox and clicks 'Save'.
The manager enters the needed information and The manager logs into the office key management system,
clicks 'Terminate Contract'. looks up the user’s key, puts a check in the 'Disabled' check
box and clicks 'Save'.
The system automatically updates the employee
records, revokes the employee’s user credentials The manager sends an e-mail to the payroll department
and electronic office key and sends a notification to notifying them that the employee has quit.
the payroll system.
But what about the concepts that live
inside a Bounded Context ?

• Each of the circled concepts that you see inside these two Bounded Contexts is an
Aggregate.
• The one concept not circled—Discussion —is modeled as a Value Object.
Entity
▪ An entity is an object whose identity is of importance.
▪ unique ID that is assigned when the entity is created and remains unchanged throughout the
lifespan of the entity
▪ Two entities of the same type and with the same ID are considered the same entity.
▪ Entities are mutable.
▪ Carefully model the setters of the entities.
What is a Value Object?
▪A Value Object models an immutable conceptual whole. Within the
model the Value is just that, a value.
▪ Instead of changing the state of the value object, you replace it with a new instance.

▪ Unlike an Entity , it does not have a unique identity, and


equivalence is determined by comparing the attributes encapsulated
by the Value type.
▪ The Value Object is not a thing but is often used to describe,
quantify, or measure an Entity.
▪ The Value Object concept is also widely used in non-DDD projects.
Value Objects(2)
▪ Value objects are not only containers of data - they can also contain business logic.
▪ Due to their immutability, business operations are both thread-safe and side-effect free.
▪ Try to make the value objects as small and as coherent as possible.
▪ this makes them easier to maintain and reuse.

▪ Adheres to ubiquitous language



Money Value Object
public class Money implements Serializable, Comparable<Money> {
private final BigDecimal amount;
private final Currency currency; // Currency is an enum or another value object
public Money(BigDecimal amount, Currency currency) {
this.currency = Objects.requireNonNull(currency);
this.amount = Objects.requireNonNull(amount).setScale(currency.getScale(), currency.getRoundingMode());
}
public Money add(Money other) {
assertSameCurrency(other);
return new Money(amount.add(other.amount), currency);
}
public Money subtract(Money other) {
assertSameCurrency(other);
return new Money(amount.subtract(other.amount), currency);
}
private void assertSameCurrency(Money other) {
if (!other.currency.equals(this.currency)) {
throw new IllegalArgumentException("Money objects must have the same currency");
}
}
public boolean equals(Object o) {
// Check that the currency and amount are the same
}
public int hashCode() {
// Calculate hash code based on currency and amount
}
public int compareTo(Money other) {
// Compare based on currency and amount
}
}
Entity or Value
Object?
▪ Value object - PersonId - is used
for the entity ID
▪ PersonName, LocalDate are value
objects
▪ Instead of using a setter for
changing the name, we use a
business method that also stores
the change in an event log,
together with the reason for why
the name was changed.
▪ getter for retrieving the history of
name changes
▪ equals and hashCode only check
the entity ID.
What is an Aggregate?
▪ Each Aggregate is composed of one or more Entities , where one Entity is called the Aggregate
Root.
▪ Transactional integrity is always maintained inside an aggregate.
▪ Aggregates may also have Value Objects composed on them.
▪ As you see here, Value Objects are used inside both Aggregates
Two important
restrictions concerning
aggregates
▪ An aggregate can be referenced
from the outside through its root
only. Objects outside of the
aggregate may not reference any
other entities inside the
aggregate.
▪ The aggregate root is responsible
for enforcing business invariants
inside the aggregate, ensuring that
the aggregate is in a consistent
state at all times.
Root Entity
▪ The Root Entity of each Aggregate owns all the other elements clustered inside it.
▪ The name of the Root Entity is the Aggregate ’s conceptual name.
▪ You should choose a name that properly describes the conceptual whole that the Aggregate
models.
The aggregate root enforces business
invariants in different ways:

All state-altering operations are performed through the aggregate


root.

State-altering operations on local entities are allowed, but they


notify the aggregate root whenever they change.
Aggregate Design Guidelines
1. Protect business invariants inside Aggregate boundaries.
2. Design small Aggregates.
3. Reference other Aggregates by identity only.
4. Update other Aggregates using eventual consistency.
1. Protect business invariants inside
Aggregate boundaries.
▪ Invariants — Invariants are generally business
rules/enforcements/requirements that you
impose to maintain the integrity of an object at
any given time.
▪ Here’s the BacklogItem Aggregate. There is a
business rule that states, “When all Task
instances have hoursRemaining of zero, the
BacklogItem status must be set to DONE .” Thus,
at the end of a transaction this very specific
business invariant must be met. The business
requires it.
2. Design small Aggregates.
Rule 3: Reference Other Aggregates by
Identity Only
▪ In this example we see that
BacklogItem , Release , and Sprint
all reference Product by holding a
ProductId .
▪ This helps keep Aggregates small
and prevents reaching out to modify
multiple Aggregates in the same
transaction.
Rule 4: Update Other Aggregates Using
Eventual Consistency
▪ the data in the application will eventually become consistent, but there will be times when not
all parts of the system are in sync with each other and that is perfectly fine.
▪ requires a different way of thinking but will, in turn, result in a system that is more resilient and
more scalable than a system that requires strong consistency only.
Right-Sizing Aggregates
1. Start by creating every Aggregate with just one Entity , which will serve as the Aggregate Root.
Don’t even dare to place two Entities in a single boundary. That opportunity will come soon enough.
Populate each of the Entities with the fields/attributes/properties that you believe are most closely
associated with the single Root Entity. One big hint here is to define every field/attribute/property
that is required to identify and find the Aggregate , as well as any additional intrinsic
fields/attributes/properties that are required for the Aggregate to be constructed and left in a valid
initial state.
2. Now place your focus on the first rule of Aggregate design, “Protect business invariants inside
Aggregate boundaries.” You have already asserted by the previous step that at a minimum all the
intrinsic fields/attributes must be up-to-date when the single-Entity Aggregate is persisted. But now
you need to look at each of your Aggregates one at a time. As you do so for Aggregate A1, ask the
Domain Experts if any other Aggregates you have defined must be updated in reaction to changes
made to Aggregate A1. Make a list of each of the Aggregates and their consistency rules, which will
indicate the time frames for all reaction-based updates. In other words, “Aggregate A1” would be
the heading of one list, and other Aggregate types would be listed under A1 if they will be updated in
reaction to A1 updates.
Right-Sizing Aggregates (2)
3. Now ask the Domain Experts how much time may elapse until each of
the reaction-based updates may take place. This will lead to two kinds of
specifications: (a) immediately, and (b) within N
seconds/minutes/hours/days. One possible way to find the correct business
threshold is by presenting an exaggerated time frame (such as weeks or
months) that is obviously unacceptable. This will likely cause business
experts to respond with an acceptable time frame.
4. For each of the immediate time frames (3a), you should strongly consider
composing those two Entities within the same Aggregate boundary. That
means, for example, that Aggregate A1 and Aggregate A2 will actually be
composed into a new Aggregate A[1,2]. Now Aggregates A1 and A2 as they
were previously defined will no longer exist. There is only Aggregate A[1,2].
5. For each of the reacting Aggregates that can be updated following a given
elapsed time (3b), you will update these using the fourth rule of Aggregate
design, “Update other Aggregates using eventual consistency.”
Tactical Design with
Domain Events
CHAPTER 6
Introduction to Domain Events
▪ Domain events describe thing that happen and change the state of the model.
▪ A domain event is anything that happens in the domain model that may be of interest to other
parts of the system.
▪ Domain events can be:
▪ coarse-grained (e.g. a specific aggregate root is created, or a process is started)
▪ fine-grained (e.g. a particular attribute of a particular aggregate root is changed)
▪ Domain events typically have the following characteristics:
▪ They are immutable (after all, you can’t change the past).
▪ They have a timestamp when the event in question occurred.
▪ They may have a unique ID that helps to distinguish one event from another. This depends on the type
of event and how the events are distributed.
▪ They are published by aggregate roots or domain services (more about those later).
Few notes about Domain Events…
▪ It’s important that the modified Aggregate and the Domain Event be saved together in the
same transaction.
▪ Once your Domain Event is saved to the event store, it can be published to any interested
parties.
Distributing Domain Events
▪ Domain events are only usable if you have a reliable way of distributing them to listeners.
▪ Distribution through a message queue

▪Distribution through an event log


Example…
Event Sourcing
▪ Event Sourcing can be described as persisting all Domain Events that have occurred for an
Aggregate instance as a record of what changed about that Aggregate instance.
▪ The event store is just a sequential storage collection or table where all Domain Events are
appended.
▪ One of the greatest advantages of using Event Sourcing is that it saves a record of everything
that has ever happened in your Core Domain.
Other Domain Objects
Repositories
▪ A repository is a persistent container of aggregates.
▪ Any aggregate that is saved into a repository can be retrieved from there at a later time.
▪ The repository should have the following capabilities:
▪ Capability to save an aggregate in its entirety in some kind of data storage
▪ Capability to retrieve an aggregate in its entirety based on its ID
▪ Capability to delete an aggregate in its entirety based on its ID
▪ In practice, a repository is a domain-aware interface into an external data storage such as a relational
database, a NoSQL database, a directory service or even a file system.
▪ A collection-oriented repository aims to mimic an in-memory collection of objects.
▪ collection oriented repository will have methods such as add() and remove() but no methods for saving.
▪ A persistence-oriented repository acts as a facade for an external persistence solution.
▪ It contains methods such as insert(), update() and delete().
Collection vs Persistence oriented repository
Collection-oriented repository Persistence oriented repository
Domain Services
▪ Both value objects and entities can (and should) contain business logic.
▪ However, there are scenarios where a piece of logic simply does not fit into one particular value
object or one particular entity.
▪ Solution: domain service.
▪ Domain services have the following characteristics:
▪ They are stateless
▪ They are highly cohesive (meaning they are specialized in doing one thing and one thing only)
▪ They contain business logic that does not naturally fit elsewhere
▪ They can interact with other domain services and to some extent with repositories
▪ They can publish domain events
▪ Its simplest form: a domain service can be a utility class with a static method in it.
▪ Its advanced form: domain services may be implemented as singletons that have other domain
services and repositories injected into them.
Code example…
Application Services
▪ An application service acts as a facade through which clients will interact with the domain
model.
▪ Application services have the following characteristics:
▪ They are stateless.
▪ They enforce system security.
▪ They control the database transactions.
▪ They orchestrate business operations but do not make any business decisions (i.e., they do not contain
any business logic)
Application Services (2)
Statelessness
Application Services (3)
Security Enforcement
Application Services (4)
Transaction Management
Application Services (5)
Orchestration 1. The application service method runs inside a transaction.
2. The application service method can be accessed by any user.
3. We invoke a JSR-303 validator to check that the incoming
registration request contains all the necessary information. If
the request is invalid, we throw an exception that will be
reported back to the user.
4. We invoke a domain service that will check if there already is
a customer in the database with the same information. If that
is the case, the domain service will throw an exception (not
shown here) that will be propagated back to the user.
5. We invoke a domain factory that will create a new Customer
aggregate with information from the registration request
object.
6. We invoke a domain repository to save the customer and
returns the newly created and saved customer aggregate
root.
Application service != Domain service
▪The application service acts as the middleman between the isolated domain
model and the rest of the world.
▪ The domain service is only responsible for making business decisions
whereas an application service is only responsible for orchestration (finding the
correct objects and invoking the correct methods in the correct order).
Factories
▪ The factories are responsible for creating new aggregates.
▪ You will typically need a separate factory in the following cases:
▪ Business logic is involved in the creation of the aggregate.
▪ The structure and content of the aggregate may be very different depending on the input data.
▪ The input data is so extensive that the builder pattern (or something similar) is needed.
▪ The factory is translating from one bounded context to another.

▪ A factory can be a static factory method on the aggregate root class or a separate factory class.
▪ The factory can interact with other factories, repositories and domain services but must never
alter the state of the database (so no saving or deleting).
Code example…
Modules

Modules in DDD correspond to packages in Java and namespaces


in C#.

A module can correspond to a bounded context, but typically, a


bounded context will have multiple modules.
Modules (2)
Group classes by process and
aggregate.
Why is Tactical Domain-Driven Design
Important?
▪ Tactical domain-driven design offers solutions to a set of problems that you typically run into,
especially when building enterprise software.
▪Offers many tools to avoid data inconsistency problems.
▪ The concept of value-object can be used even in non-DDD projects.
▪ Value Objects make the code easier to read and understand since it brings context to your attributes.
▪ The immutability also tends to make complex things simpler.
▪ Avoid anemic data model (only getters and setters without any business logic).
▪ This helps in keeping the data consistent and avoids strange side effects and optimistic locking
exceptions when the same entity is being updated through different mechanisms.
▪ Domain events are useful to decouple your code.
▪ Don’t overuse events in your application - your code will become more difficult to understand and
debug.

You might also like