Anatomy of an Aggregate

An Aggregate is an Entity that may hold other Entities and Value Objects. The parent Entity is known as the root Entity.

A single Entity without any child Entities or Value Objects is also an Aggregate by itself. That's why in some books, the term Aggregates is used instead of the term Entity. When we use them here, Entity and Aggregate mean the same thing.

The main goal of an Aggregate is to keep your Domain Model consistent. Aggregates centralize most of the business rules. Aggregates are persisted atomically in your persistence mechanism. No matter how many child Entities and Value Objects live inside the root Entity, all of them will be persisted atomically, as a single unit. Let's see an example.

Consider an e-commerce application, website, and so on. Users can place orders, which have multiple lines that define what product was bought, the price, the quantity, and the line total amount. An order has a total amount too, which is the sum of all line amounts.

What could happen if you update a line amount but not the order amount? Data inconsistency. To fix this, any modification to any Entity or Value Object within the Aggregate is performed through the root Entity. Most PHP developers we've worked with are more comfortable building objects and then handling their relationships from the client code, rather than pushing the business logic inside the Entities:

$order = ...
$orderLine = new OrderLine(
'Domain-Driven Design in PHP', 24.99
);
$order->addOrderLine($orderLine);

As seen in the previous code example, newbie or even average developers generally build child objects first and then relate them to the parent object using a setter. Consider the following approach:

$order = ...
$orderLine = $order->addOrderLine(
'Domain-Driven Design in PHP', 24.99
);

Or, consider this approach:

$order = ...
$order->addOrderLine(
'Domain-Driven Design in PHP', 24.99
);

These approaches are very interesting because they follow two Software Design principles: Tell-Don't-Ask and Law of Demeter.

According to Martin Fowler:

Tell-Don't-Ask is a principle that helps people remember that object-orientation is about bundling data with the functions that operate on that data. It reminds us that rather than asking an object for data and acting on that data, we should instead tell an object what to do. This encourages to move behavior into an object to go with the data.

According to Wikipedia:

The Law of Demeter (LoD) or principle of least knowledge is a design guideline for developing software, particularly object-oriented programs. In its general form, the LoD is a specific case of loose coupling...and can be succinctly summarized in each of the following ways:

  • Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
  • Each unit should only talk to its friends; don't talk to strangers.
  • Only talk to your immediate friends.

The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents), in accordance with the principle of "information hiding".

Let's continue with the order example. You've already learned how to run operations through the root Entity. Now let's update a product quantity of a line in an order. This increases the quantity, the line total amount, and the order amount. Great! Now it's time to persist the order with the changes.

If you're using MySQL, you can imagine that we'll need two UPDATE statements: one for the orders table, and another one for the order_lines table. What could happen if these two queries aren't performed inside a transaction?

Let's assume that the UPDATE statement that updates the line order works properly. However, the UPDATE on the order total amount fails due to network connectivity issues. In such a scenario, you would end up with a data inconsistency in your Domain Model. Transactions help you keep this consistency.

If you're using Elasticsearch, the situation is a bit different. You can map the order with a JSON document that holds order lines internally, so just a single request is needed. However, if you decide to map the order with one JSON document and each of its order lines with another JSON document, you're in trouble, as Elasticsearch doesn't support transactions. Ouch!

An Aggregate is fetched and persisted using its own Chapter 10Repositories. If two Entities don't belong to the same Aggregate, both will have their own Repository. If a true business invariant exists and two Entities belong to the same Aggregate, you'll only have one Repository. This Repository will be the one for the root Entity.

What are the cons of Aggregates? The problem when dealing with transactions is the possibility of performance issues and operation errors. We'll explore this in depth soon.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset