Two useful concepts to simplify your application
This is part 4 of a summary series I am doing on a podcast I co-host, you can find it at: www.iterationpodcast.com
Every object has a lifecycle. It exists, and then it doesn’t. Create, read, update, delete. Think about a blog application and two objects: blog post object and blog comment object. It is important to group these two objects together as they are related. For example, when deleting a post, what happens to the comments? If they are not grouped with the blog post object, they will end up as orphan objects that clutter the database.
Aggregates are groupings of related objects. In the blog application example, blog post object and blog comment object form an aggregate.
“Cluster the entities and value objects into aggregates and define boundaries around each. Choose one entity to be the root of each aggregate and control all access to the objects inside the boundary through the root”
— Eric Evans in Domain Driven Design
A blog post object would be the entity and the root of the aggregate. To prevent orphan comments, comment object has to be within the boundaries of the aggregate.
Guidelines for managing aggregates
A delete operation must remove everything within the aggregate boundaries. For example, deleting a blog post must also remove all the comments related to the deleted blog post.
When referencing aggregate objects from the other parts of a program, it is important to reference the root. Think about an aggregate for a car with the car as the root entity and tire as a value object within the aggregate. From another part of the program, even if we want to access the tire object, we should look up car instead of tire.
When there are multiple aggregates grouping the same object, it is important to decide what the root entity is, to prevent errors arising from two aggregates stepping on each other’s toes. In the case of two aggregates stepping on each other’s toes, database locking (locking up higher up in the parent-child hierarchy, in the aggregate root) is a method of preventing it from happening.
Why do aggregates matter?
- Grouping your object and categorizing them as such, makes complex associations manageable.
- Makes it easy to garbage collect, preventing orphan records.
- It makes responsibility clear.
- Enables talking on a higher level about databases.
“There are some cases in which an object creation and assembly corresponds to a milestone significant in the domain, such as “open a bank account.” But object creation and assembly usually have no meaning in the domain… To solve this problem, we have to add constructs to the domain design that are not entities, value objects, or services.”
— Eric Evans in Domain Driven Design
The constructors Evans is talking about are factories. At some point in our development process, we might need something that is responsible for creating other things. Let’s say we want to open a bank account. We need lots of software objects associated with each other, such as address object, user object and so on. A factory can handle the creation of all these objects associated with the bank account.
What the factory generates is important, but how it happens has no relevance to your domain. But the result of the factories do matter to the domain. The use of factories cleans up the code and hides implementation.
Factories vs. Services
Factories and services are similar in the sense that they both take an object and put out an object. Factories are a type of service. Factories always spit out a new object, whereas services don’t necessarily do so. With factories, we make a single request and get an object out, with the output being predictable. A service should handle the factory and the factory should handle the logic of creation.
P.S. This article was written with help from Senem Soy based on content from our podcast episode. Thanks Senem!