Laravel Event Projector and Event Generation Concept

Original author: Paul Redmond
  • Transfer


Translation of the article was prepared for students of the professional course "Framework Laravel"




Frek Van der Herten and the Spatie team have worked hard for a long time on the Laravel Event Projector, a package that allows you to apply the Event Sourcing concept to the Laravel framework. And finally, the first stable version (v1.0.0) is available!


You can install Event Projector into your project using composer and, thanks to automatic package detection in Laravel, get to work immediately after publishing package migrations and configuration!


composer require spatie/laravel-event-projector:^1.0.0

Event Projector requires PHP 7.2, so your application must support the latest version of PHP, although the Laravel framework itself needs a PHP version of at least 7.1.3.


What is “event generation”


In the article Generating Events, this concept is described as follows:


Instead of storing the current state of the data in the domain, use to record the entire sequence of actions performed on this data, the storage operating in the append-only mode is only adding data. The storage acts as a system of records and can be used to materialize domain objects. This allows us to simplify tasks in complex subject areas, since there is no need to synchronize the data model and subject area, which leads to improved scalability, increased productivity and operational efficiency. This approach ensures the consistency of transactional data, and also allows you to keep a complete change log and history, making it possible to carry out compensatory actions.

I recommend that you read the entire article. It provides an excellent description of this template, voices considerations for proper use, and describes situations where it may be useful.


I also like the explanation of the concept of event generation, which is given in the introduction to the documentation for Event Projector:


The generation of events is the same with respect to data than Git is with respect to code. Most applications only store their current state in the database. A significant amount of useful information is lost - you do not know how the application reached this state.

The concept of event generation is an attempt to solve this problem by saving all the events that occur in your application. The state of the application is formed as a result of listening to these events.

To facilitate understanding, consider a small example from life. Imagine that you are a bank. Your customers have accounts. Keeping only information about the account balance is not enough. You must also remember all transactions. When using the event generation template, the account balance will be not just a separate field in the database, but a value calculated based on the stored transaction information. This is just one of the many benefits that the concept of event generation offers.

This package is designed to familiarize users of the Laravel framework with the concept of event generation and facilitate its use in practice.

It seems that Event Projector helps to simplify the work with the event generation pattern. The documentation also helped me figure out how to use this approach as part of the concepts of the Laravel framework that I already know.


Laravel Eventing Basics


To understand how event generation in the Event Projector package works and what components are involved in this process, you should read the Creating the first projector section .


At a high level (forgive me if I’m not entirely correct in my assessment, since the generation of events is a new concept for me) the generation of events using the Event Projector includes:


  • Eloquent Models
  • Events implementing the ShouldBeStored interface
  • Projector Classes

An example of a model class with a static method createWithAttributesgiven in the documentation:


namespace App;
use App\Events\AccountCreated;
use App\Events\AccountDeleted;
use App\Events\MoneyAdded;
use App\Events\MoneySubtracted;
use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;
class Account extends Model
{
    protected $guarded = [];
    protected $casts = [
        'broke_mail_send' => 'bool',
    ];
    public static function createWithAttributes(array $attributes): Account
    {
        /*
         * Давайте сгенерируем универсальный уникальный идентификатор (uuid). 
         */
        $attributes['uuid'] = (string) Uuid::uuid4();
        /*
         * Счет будет создан в этом событии на основе сгенерированного uuid.
         */
        event(new AccountCreated($attributes));
        /*
         * Обращение к созданному счету будет происходить по этому uuid.
         */
        return static::uuid($attributes['uuid']);
    }
    public function addMoney(int $amount)
    {
        event(new MoneyAdded($this->uuid, $amount));
    }
    public function subtractMoney(int $amount)
    {
        event(new MoneySubtracted($this->uuid, $amount));
    }
    public function delete()
    {
        event(new AccountDeleted($this->uuid));
    }
    /*
     * Вспомогательный метод для быстрого обращения к счету по uuid.
     */
    public static function uuid(string $uuid): ?Account
    {
        return static::where('uuid', $uuid)->first();
    }
}

Here it is necessary to pay attention to events AccountCreated, MoneyAdded, MoneySubtractedand AccountDeleted, accordingly initiated for opening an account, add money to the account, withdraw money from the account and delete the account.


Below is an example of an event MoneyAdded(adding money to an account). The ShouldBeStored interface tells the Event Projector package that this event should be saved:


namespace App\Events;
use Spatie\EventProjector\ShouldBeStored;
class MoneyAdded implements ShouldBeStored
{
    /** @var string */
    public $accountUuid;
    /** @var int */
    public $amount;
    public function __construct(string $accountUuid, int $amount)
    {
        $this->accountUuid = $accountUuid;
        $this->amount = $amount;
    }
}

And finally, a partial example of an event handler inside the projector class to replenish funds in the account (my favorite type of banking event):


public function onMoneyAdded(MoneyAdded $event)
{
    $account = Account::uuid($event->accountUuid);
    $account->balance += $event->amount;
    $account->save();
}

To link all this together, consider an example from the documentation on creating a new account and adding funds:


Account::createWithAttributes(['name' => 'Luke']);
Account::createWithAttributes(['name' => 'Leia']);
$account = Account::where(['name' => 'Luke'])->first();
$anotherAccount = Account::where(['name' => 'Leia'])->first();
$account->addMoney(1000);
$anotherAccount->addMoney(500);
$account->subtractMoney(50);

I suspect that the balance in the accounts of Luke and Leia is expressed in galactic standard loans, although in general I doubt that they would openly conduct such operations.


My attention was also attracted by the following advantage of generating events using this package, which is indicated in the documentation:


An interesting point when working with projectors - they can be created after events have occurred. Imagine that the bank wants to receive a report on the average balance in each account. In this case, it will be enough to create a new projector, play all the events and get this data in the end.

The very idea of ​​creating new projectors at the end of events seems very promising. It turns out that you do not have to get all the “right” data in advance, but you can come to the result iteratively.


As for performance, according to the documentation, access to projectors "happens very quickly."


Want to know more?


To get started, I recommend that you read the Event Projector documentation and make sure you have the latest version of PHP 7.2 installed. The Spatie team also uploaded a sample application on Laravel , demonstrating the capabilities of the Event Projector package.


You can download the code, mark the repository with an asterisk, and also contribute to the development of the Event Projector project on GitHub . At the time of writing, the project Event Projector has already 15 participants, whose efforts made possible the stable release of 1.0. Great job, Spatie! I'm sure Event Projector will be another great package that will be appreciated by the Laravel community.


Also popular now: