What is an Aggregate in an event sourced application

What is it, why to use it and how.

aggregate

In this article I will cover what is an aggregate, why to use it and how. But before I deep dive in that, first we need to tackle three other concepts: Value Object,  Entity and Event or in other words the tactical tools of the Domain Driven Design.

The use of an Aggregate in combination with Events is more suitable to Event Driven systems. So let’s keep in mind that we are in an event sourced application and that our use case in the registration of a customer.

Value Object

Value Object is “an immutable type that is distinguishable only by the state of its properties.”. [1]

But what that actually means? Let’s have a look at an example:

class Name
{
    /** @var string */
    private $firstName;

    /** @var string */
    private $lastName;

    public function __construct(string $firstName, string $lastName)
    {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function getLastName(): string
    {
        return $this->lastName;
    }
}

Above we have a class Name with two properties. Once the object is created, its properties can not be changed, in other words the object is immutable.
Also the object does not have a unique identifier, which means that two objects with the same properties are equal!

$name1 = new Name('foo', 'bar');
$name2 = new Name('foo', 'bar');

var_dump($name1 == $name2); // true

We could also have larger value object that is combined by smaller value objects eg. The value object Price which is represented by two value objects, Amount and Currency.
Now that we have a full grasp of what a value object is, we can move on to the Entity and we will be one step closer to understanding the aggregate.

Entity

An Entity “has a unique identifier and remains distinct even if its properties are identical”[1]. Entities are concepts, whose instances are uniquely identifiable by an immutable identifier and contain a set of instances of value objects which can be changed within the entity. It is easy to identify the concept of entities in a conversation. The domain experts will refer to it as a single, identifiable item, eg. Customer, Invoice, Order, Article, Post.

class Customer
{
    /** @var string */
    private $identity;

    /** @var Name */
    private $name;

    public function __construct(Name $name, string $identity = null)
    {
        $this->name = $name;
        $this->identity = isset($identity) ? $identity : uniqid('', true);
    }

    public function getIdentity(): string
    {
        return $this->identity;
    }

    public function getName(): Name
    {
        return $this->name;
    }
}

In this particular example we want to have the identity optional.

The Customer Entity when created gets a value object Name as a variable and it creates a unique identifier. If two Entities have the same name, they won’t be the same. Let’s see what happens in action:

$name = new Name('John', 'Doe');

$customer1 = new Customer($name, 'identity1');
$customer2 = new Customer($name, 'identity2');

var_dump($customer1 == $customer2);     // false

echo $customer1->getIdentity();         // identity1
echo $customer2->getIdentity();         // identity2

Note: There is a more secure way to generate unique identifiers. Most common practice is to use the ramsey/uuid library.

An event (a.k.a domain event) is a simple data object, that notifies the system that something has happened in the domain. It describes something that occured in the past which no longer cannot alter – no one can change history. All the data that it contains are valid. The reason is, the aggregate is responsible to generate the event and to do so, first validates the data that it receives. The role of the aggregate will be explained in more detail later on.

Here is an example of a domain event which implements the following Interface:

interface Event
{
   public function getEventIdentity(): string;
 
   public function getName(): string;
}

class CustomerRegisteredEvent
{
    /** @var string */
    private $eventIdentity;

    /** @var string */
    private $customerIdentity;

    /** @var string */
    private $firstName;

    /** @var string */
    private $lastName;
   
    public function __construct(
        string $eventIdentity,
        string $customerIdentity,
        string $firstName,
        string $lastName
    ){
        $this->eventIdentity = $eventIdentity;
        $this->customerIdentity = $customerIdentity;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    // GETTERS

    public function getName(): string
    {
       return self::class;
    }
}

Let’s create an event and see what it contains:

$event = new CustomerRegisteredEvent(
    'eventIdentity',
    'customerIdentity',
    'John',
    'Doe'
);

var_dump($event);

This is the result of the event:

object(CustomerRegisteredEvent)#7 (5) {
 ["eventIdentity":"CustomerRegisteredEvent":private]=>
  string(23) "eventIdentity"
  ["customerIdentity":"CustomerRegisteredEvent":private]=>
  string(16) "customerIdentity"
  ["firstName":"CustomerRegisteredEvent":private]=>
  string(4) "John"
  ["lastName":"CustomerRegisteredEvent":private]=>
  string(3) "Doe"
}

I can’t but note that, the events should contain if possible only primitive variables and not value objects.

If is absolutely necessary to construct an event with a value object then the value object has to be able to deserialize and serialize properly.

Aggregate

And now ladies and gentlemen get ready for

the extraordinary…
the one and only…
the truly amazing…

*DRUMROLL*

Aggregate!

Let’s say you want to buy a table from Ikea. Do you get a table? Not really, you get a bunch of components and instructions in a box that you have to put together yourself. Let’s say you take out all the wooden parts, screws etc from the box and put them in a pile. Do you have a table? No. You have the components of a table but you need to assemble them according to some rules in order to end up with a table.

The table is an aggregate, that is a group of components ‘held’ together by (assembly) rules, in order to act as a single thing. Both components and rules are what makes an aggregate. A pile of parts doesn’t make a table, just some assembly rules don’t mean anything, we need all of them to create our table. [2]

An aggregate is a collection of concepts and business rules. Its basic priority is to apply any changes to the business state. These changes have to comply with the business rules that have been defined. In other words “the aggregate is a model that represents all the relevant information we need to change something.”[2]

So make things a bit clearer let’s see our example below:

class Registration
{
   /** @var Event[] */
   private $unrecordedEvents = [];
 
   /** @var string */
   private $customerIdentity;

   private function recordThat(Event $event)
   {
       $this->apply($event);

       $this->unrecordedEvents[] = $event;
   }

   protected function apply(Event $event)
   {
       $applyMethod = [$this, 'apply' . $event->getName()];

       if (is_callable($applyMethod)) {
           $applyMethod($event);
       }
   }

   public function getUnrecordedEvents(): array
   {
       return $this->unrecordedEvents;
   }

   public static function create(
        string $customerIdentity,
        string $firstName,
        string $lastName
   ): self {
        // VALIDATION FOR FIRSTNAME AND LASTNAME

        $eventIdentity = 'eventIdentity';
        $event = new CustomerRegisteredEvent(
            $eventIdentity,
            $customerIdentity,
            $firstName,
            $lastName
        );
     
        $registration = new static();
        $registration->recordThat($event);
        $registration->apply($event);
     
        return $registration;
   }
 
   private function applyCustomerRegisteredEvent(CustomerRegisteredEvent $event)
   {
       $this-$customerIdentity = $event->getCustomerIdentity();
   }
}

So now let’s assume that our aggregate gets a command to register a customer.

$registration = Registration::create('customerIdentity', 'John', 'Doe');
$events = $registration->getUnrecordedEvents();

var_dump($events);
array(1) {
  [0]=>
  object(CustomerRegisteredEvent)#1 (5) {
   ["eventIdentity":"CustomerRegisteredEvent":private]=>
    string(23) "eventIdentity"
    ["customerIdentity":"CustomerRegisteredEvent":private]=>
    string(23) "customerIdentity"
    ["firstName":"CustomerRegisteredEvent":private]=>
    string(4) "John"
    ["lastName":"CustomerRegisteredEvent":private]=>
    string(3) "Doe"
    ["recordedAt":"CustomerRegisteredEvent":private]=>
  }
}

We get an array of events that the aggregate raised, that they show what happened in the business state.

So what is next?

Next the recorded event will be picked up by any Event Listeners that might be interested in that event and apply the appropriate to use case changes using the entities and the value object of the Domain.

Conclusion

Let’s see now how everything ties up together like a symphony.

screenshot-docs.google.com-2018.06.16-14-28-36

A command is sent through the command bus to a handler. The handler calls the aggregate and the validates all the data that it receives and raises an event on success. The handler retrieves the event from the aggregate and send it to the event bus. The event listener that is interested in the event will get the event and it will create/update etc the entity through a repository.

Applying those concepts to your application you will immediately see the advantages:

  • Segregation of responsibilities
  • Immediate separation of the application to layers (Hexagonal Architecture)
  • Each business case can its own relevant model even if it involves the same concept
  • Clear distinction of the business rules
  • Easy to maintain

Even though it is quite difficult to grasp in the beginning, it is totally an asset to practice DDD.

DDD is not about object oriented design, but it is about paying attention to the domain experts and the stakeholders. The concepts that I explained in this article are just tools that we could use to solve the use cases that our stakeholder need implemented.

Bibliography

Other Sources:

Libraries:

About the Author: Vicky Ftochogianni

Experienced PHP Backend Developer with a passion for Clean Code and a demonstrated history of working in the web developement industry. Skilled in Test Driven Development, OOP, SOLID principles, Restful APIs, and event driven services. Strong engineering professional with a Bachelor of Science (BSc) focused on Electronic Computing Systems Engineering.