Translation of the article prepared for students of the professional course "Framework Laravel"
Frek van der Herten (Freek Van der Herten) and the Spatie team have long worked on the Laravel Event Projector, a package that allows you to apply the concept of event generation (Event Sourcing) in the Laravel framework. And finally, the first stable version (v1.0.0) is available!
You can install an Event Projector into your project using the composer and, thanks to automatic package discovery in Laravel, you can start working immediately after publishing the package migrations and configuring!
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 requires PHP version 7.1.3 or higher.
In the Event Generation Template article , this concept is described as follows:
Instead of storing the current state of the data in the domain (domain), use to record the entire sequence of actions performed on this data, the repository operating in the append-only mode - just adding data. The storage acts as a system of record and can be used to materialize domain objects. This allows you to simplify tasks in complex subject areas, since there is no need to synchronize the data model and the subject area, which leads to improved scalability, increased productivity and operational efficiency. This approach ensures consistency of transactional data, and also allows you to keep a complete change log and history, making it possible to implement compensating actions.
I recommend reading the entire article. It gives an excellent description of this template, voiced considerations for proper use, and also describes situations where it may be useful.
I also like the explanation of the concept of event generation that is given in the introduction to the documentation for the Event Projector:
The generation of events is in relation to the data the same as Git is in relation to the code. Most applications store only their current state in the database. A significant amount of useful information is lost - you do not know how the application has reached this state.
The concept of generating events is an attempt to solve this problem by saving all the events that occur in your application. Application state is formed as a result of listening to these events.
To facilitate understanding, consider a small example from life. Imagine you are a bank. Your customers have accounts. It is not enough to store only information about the account balance. It is also necessary to memorize 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 acquaint users of the Laravel framework with the concept of generating events and facilitate its use in practice.
It seems that Event Projector helps to simplify work with the event spawning pattern. The documentation also helped me figure out how to use this approach within the framework of the concepts of the Laravel framework, with which I am already familiar.
In order to understand how event generation in the Event Projector package works and what components are involved in this process, you should familiarize yourself with the section Creating the First Projector .
At a high level (forgive me, if 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:
An example of a model class with a static method createWithAttributes
, given 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 you need to pay attention to the events AccountCreated
, MoneyAdded
, MoneySubtracted
and AccountDeleted
, initiated respectively for opening an account, adding money to an account, withdrawing money from an account and deleting an account.
Below is an example of a MoneyAdded
event (adding money to an account). The ShouldBeStored interface indicates to 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 a projector class for replenishing funds in an account (my favorite type of banking event):
public function onMoneyAdded(MoneyAdded $event) { $account = Account::uuid($event->accountUuid); $account->balance += $event->amount; $account->save(); }
To tie it all together, consider an example from the documentation for 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 of funds in the accounts of Luke and Lei is expressed in the galactic standard credits, although in general I doubt that they would openly conduct such operations.
My attention was also attracted by the following advantage of triggering events using this package, as indicated in the documentation:
An interesting point when working with projectors - you can create them after the events occurred. Imagine that a bank wants to receive a report on the average balance on each account. In this case, it will be enough to create a new projector, reproduce all the events and get this data as a result.
The very idea of ​​creating new projectors at the end of events seems very promising. It turns out that you do not need to receive all the "correct" data in advance, but you can iterate over the result.
As for performance, according to the documentation, the call to the projectors “happens very quickly”.
First, I recommend that you read the documentation for the Event Projector and make sure you have the latest version of PHP 7.2 installed. The Spatie team also posted 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 project Event Projector on GitHub . At the time of writing this article, 15 participants are already working on the Event Projector project, with the efforts of which a stable release 1.0 was possible. Great job, Spatie! I am sure that Event Projector will be another great package that will be appreciated by the Laravel community.
Source: https://habr.com/ru/post/460683/
All Articles