★ Event store optimizations in laravel-event-sourcing
Tags: laravel, php
Published: 2020-01-02 23:37:53

---
title: ★ Event store optimizations in laravel-event-sourcing
published: true
date: 2019-09-23 11:12:54 UTC
tags: laravel, php
canonical_url: https://freek.dev/1457-event-store-optimizations-in-laravel-event-sourcing
---

About a year ago, we [released](https://freek.dev/1101-introducing-laravel-event-projector-the-easiest-way-to-get-started-with-event-sourcing-in-a-laravel-app) laravel-event-projector. It focused on adding projectors, an important concept in event sourcing, to Laravel.

After the release of the package, we continually kept improving it. We added [aggregates](https://freek.dev/1326-implementing-event-sourcing-aggregates), a [way to test](https://freek.dev/1409-implementing-event-sourcing-testing-aggregates) those, a brand [new section](https://docs.spatie.be/laravel-event-sourcing/v1/getting-familiar-with-event-sourcing/introduction/) in the our documentation that explains event sourcing from scratch, and [DX improvements](https://freek.dev/1329-implementing-event-sourcing-improving-the-developer-experience) all across the board.

We now feel confident that the package is a good starting point for getting started with event sourcing in Laravel. That's why we're renaming the package to `laravel-event-sourcing`.

The package also includes some memory optimizations, which were added by my colleague [Rias](https://twitter.com/riasvdv). In this blog post, I'd like to walk you through the changes.

## Using your own event store #

In previous versions laravel-event-projector, the storage of events was tied to Eloquent. We had a `StoredEvent` model that handled the (de-)serialization of an event.

Before an aggregate can be used, it needs to be reconstituted from previous events. If you have a very active aggregate, that needs to be rebuilt using a great many events, the time serializing and deserializing `StoredEvent` models will add up.

That's why we added an interface that allows you to use any storage you want. This would allow you to create an event store in which manually build queries (for example with `DB::` statements, without using Eloquent).

```php
namespace Spatie\EventSourcing;

use Illuminate\Support\LazyCollection;

interface StoredEventRepository
{
    public function retrieveAll(string $uuid = null): LazyCollection;

    public function retrieveAllStartingFrom(int $startingFrom, string $uuid = null): LazyCollection;

    public function persist(ShouldBeStored $event, string $uuid = null): StoredEvent;

    public function persistMany(array $events, string $uuid = null): LazyCollection;

    public function update(StoredEvent $storedEvent): StoredEvent;
}
```

If you need an example, you can take a look at how we implemented [`EloquentStoredEventRepository`](https://github.com/spatie/laravel-event-sourcing/blob/17b6e875d9ebd75c9929f4e461b32bed66f38759/src/EloquentStoredEventRepository.php), which is the repository that will be used by default.

After creating your implementation, don't forget to specify the class name of your repository in [the `stored_event_repository` key of the config file](https://github.com/spatie/laravel-event-sourcing/blob/17b6e875d9ebd75c9929f4e461b32bed66f38759/config/event-sourcing.php#L51-L56).

## Memory optimizations #

You might have noticed the usage of `LazyCollection` in the `StoredEventRepository`. This is a feature [introduced](https://laravel.com/docs/6.x/collections#lazy-collections) in Laravel 6. Under the hood, it leverages PHP's generators to work with large collections, while still keeping memory use low.

Let's take a look at the implementation of `retrieveAll` in `EloquentStoredEventRepository`

```php
public function retrieveAll(string $uuid = null): LazyCollection
{
    /** @var \Illuminate\Database\Query\Builder $query */
    $query = $this->storedEventModel::query();

    if ($uuid) {
        $query->uuid($uuid);
    }

    return $query->orderBy('id')->cursor()->map(function (EloquentStoredEvent $storedEvent) {
        return $storedEvent->toStoredEvent();
    });
}
```

Notice the usage of `cursor()` here. This will significantly reduce memory usage because Laravel will, at any given moment, only hold one model in memory. Under the hood, this uses [MySQL cursors](http://www.mysqltutorial.org/mysql-cursor/). As I understand it, instead of having the results in memory of the laravel app, MySQL will hold a result set and send the results one by one to the Laravel app.

Rias performed some benchmarks using his Macbook Pro (processor: 2.3 Ghz Intel Core i5). These are the numbers for calling `retrieveAll` with 100 000 events to be retrieved.

laravel-event-projector with `Collection`:

- peak memory usage: 267MB of memory
- execution time: 7.0077209472656 seconds

laravel-event-sourcing with `LazyCollection`:

- peak memory usage: 18MB of memory
- execution time: 7.478010892868 seconds

Obviously, peak memory usage is much lower/better using `LazyCollection` at the expense of a slightly worse execution time.

## Upgrading from laravel-event-projector the package #

If you were on `v3` of the `laravel-event-projector`, upgrading to `laravel-event-sourcing` is easy. You have to perform these steps:

1. Change `"laravel-event-projector":"^3.0"` to `"laravel-event-sourcing":"^v1.0"` and run `composer update`
2. The namespace has changed, so you need to replace `Spatie\EventProjector` by `Spatie\EventSourcing` in your entire project.

If you're still on `v1` or `v2` of `laravel-event-projector` then you need to upgrade to `v3` first like described in [our upgrade guide](https://github.com/spatie/laravel-event-projector/blob/master/UPGRADING.md).

## In closing #

I'm proud of how our package grew from a simple way to handle projectors to the easiest way to get started with event sourcing in Laravel. I did not do this by myself. My colleagues [Seb](https://twitter.com/sebdedeyne) & [Rias](https://twitter.com/riasvdv) did a lot of polishing, I got some good advice from my buddy [Dries](https://twitter.com/driesvints). [Frank De Jonge](https://twitter.com/frankdejonge)'s [EventSauce](https://eventsauce.io/) package inspired some of the features.

[laravel-event-sourcing](https://docs.spatie.be/laravel-event-sourcing/v1/introduction/) isn't the first package that was built by [Spatie](https://spatie.be/). Take a look at [this big list of Laravel and PHP stuff we've opensourced](https://spatie.be/open-source/packages).