★ Implementing event sourcing: aggregates
Tags: php, laravel, opensource
Published: 2020-01-02 23:22:36

---
title: ★ Implementing event sourcing: aggregates
published: true
date: 2019-04-24 20:51:00 UTC
tags: php, laravel, opensource
canonical_url: https://freek.dev/1326-implementing-event-sourcing-aggregates
---

Recently we've [released](https://freek.dev/laravel-event-projector-v2-has-been-released) [v2 of laravel-event-projector](https://docs.spatie.be/laravel-event-projector/v2/introduction). The package is probably the easiest way to get started with event sourcing in Laravel.

One of the prominent new features is support for aggregates. While creating v2, I found it surprising that such a powerful concept could be implemented in [so little code](https://github.com/spatie/laravel-event-projector/blob/47f9d7a6479a1860e91956229a4463a706559c68/src/AggregateRoot.php). In this short blog post, I'd like to explain how aggregates are coded up.

If you don't know anything about event sourcing or don't know what aggregates are, head over to the docs of the package. It contains [an entire introduction targeted at newcomers](https://docs.spatie.be/laravel-event-projector/v2/getting-familiar-with-event-sourcing/introduction). The rest of this post assumes that you know what aggregates are and that you know how to work with them.

## Reconstituting an aggregate from previous events #

Before you can work with an aggregate, it must be reconstituted from all previous events for a given uuid. As a package user, you can do this with `MyAggreggate::retrieve($uuid)`. Here is the implementation of that function.

```php
public static function retrieve(string $uuid): AggregateRoot
{
    $aggregateRoot = (new static());
    
    $aggregateRoot->aggregateUuid = $uuid;
    
    return $aggregateRoot->reconstituteFromEvents();
}
```

The `$uuid` will be kept as an instance variable `aggregateUuid`. The actual reconstituting happens in `reconstituteFromEvents`. Let's take a look at that code.

```php
private function reconstituteFromEvents(): AggregateRoot
{
    StoredEvent::uuid($this->aggregateUuid)->each(function (StoredEvent $storedEvent) {
        $this->apply($storedEvent->event);
    });
    
    return $this;
}
```

That `::uuid` is [just a scope that defined on the `StoredEventModel`](https://github.com/spatie/laravel-event-projector/blob/47f9d7a6479a1860e91956229a4463a706559c68/src/Models/StoredEvent.php#L59-L62). That `each` call is being called on a `Builder` instance, not a `Collection`. Under the hood that `each` call will fetch all events [in a chunked way](https://twitter.com/freekmurze/status/1110274416229998592).

Let's take a look at that `apply` method. It will convert the class name of an event to a method name. `App\Event\MoneyAdded` will be converted to `applyMoneyAdded`. If such a method with that name exists on the aggregate, it will get called with the event as the argument.

```php
private function apply(ShouldBeStored $event): void
{
    $classBaseName = class_basename($event);
    
    $camelCasedBaseName = ucfirst(Str::camel($classBaseName));
    
    $applyingMethodName = "apply{$camelCasedBaseName}";
    
    if (method_exists($this, $applyingMethodName)) {
        $this->$applyingMethodName($event);
    }
}
```

And that's basically all the code needed to reconstitute an aggregate from past events.

## Recording new events #

When an aggregate is persisted all new events it recorded should be stored. This is very easy to accomplish. An aggregate offers a `recordThat` function to record new events.

```php
public function recordThat(ShouldBeStored $event): AggregateRoot
{
    $this->recordedEvents[] = $event;
    
    $this->apply($domainEvent);
    
    return $this;
}
```

That function keeps the events in array on the instance. It also immediately applies it onto the aggregate (using that `apply` method we already reviewed above).

When calling `persist` on the aggregate, all the events will be passed to the `storeMany` method of the `StoredEvent` model. The `$recordedEvents` array will be emptied so we don't record the same events should `persist` be called again on this instance of the aggregate.

```php
public function persist(): AggregateRoot
{
    StoredEvent::storeMany(
       $this->getAndClearRecoredEvents(),
       $this->aggregateUuid,
    );
           
    return $this;
}

private function getAndClearRecoredEvents(): array
{
    $recordedEvents = $this->recordedEvents;
    
    $this->recordedEvents = [];
    
    return $recordedEvents;
}
```

Because the actual storing of the events in `storeMany` isn't a responsibility of the aggregate, we're not going to go over it in detail. In short, [`storeMany`](https://github.com/spatie/laravel-event-projector/blob/47f9d7a6479a1860e91956229a4463a706559c68/src/Models/StoredEvent.php#L74-L97) will store the events, pass them to all registered projectors and will dispatch a job to pass them to all queued projectors and reactors.

And with that, we've implemented aggregates.