Перевод статьи подготовлен для студентов профессионального курса «Framework Laravel»
Фрек ван дер Хертен (Freek Van der Herten) и команда Spatie долго трудились над Laravel Event Projector, пакетом, позволяющим применять концепцию порождения событий (Event Sourcing) во фреймворке Laravel. И вот наконец доступна первая стабильная версия (v1.0.0)!
Вы можете установить Event Projector в свой проект при помощи composer и благодаря автоматическому обнаружению пакетов в Laravel приступить к работе сразу же после публикации миграций пакета и конфигурирования!
composer require spatie/laravel-event-projector:^1.0.0
Event Projector требует наличия PHP 7.2, поэтому ваше приложение должно поддерживать последнюю версию PHP, хотя самому фреймворку Laravel достаточно PHP версии не ниже 7.1.3.
Что такое «порождение событий»
В статье Шаблон порождения событий эта концепция описывается следующим образом:
Вместо того, чтобы хранить текущее состояние данных в предметной области (domain), используйте для записи всей последовательности действий, производимых над этими данными, хранилище, работающее в режиме append-only — только добавление данных. Хранилище выступает в роли системы записей (system of record) и может использоваться для материализации объектов предметной области. Это позволяет упростить задачи в сложных предметных областях, поскольку отпадает необходимость в синхронизации модели данных и предметной области, что приводит к улучшению масштабируемости, повышению производительности и оперативности работы. Подобный подход обеспечивает согласованность транзакционных данных, а также позволяет вести полноценные журнал изменений и историю, благодаря чему становится возможным осуществление компенсирующих действий.
Я рекомендую ознакомиться со всей статьей целиком. Там дается прекрасное описание этого шаблона, озвучены соображения по правильному использованию, а также описаны ситуации, где он может оказаться полезным.
Мне также нравится объяснение концепции порождения событий, которое дано во введении к документации по Event Projector:
Порождение событий является по отношению к данным тем же самым, чем Git является по отношению к коду. Большинство приложений хранят в базе данных только свое текущее состояние. Значительный объем полезной информации теряется — вы не знаете, каким образом приложение достигло этого состояния.
Концепция порождения событий — это попытка решить эту проблему путем сохранения всех событий, которые происходят в вашем приложении. Состояние приложения формируется в результате прослушивания этих событий.
Для облегчения понимания рассмотрим небольшой пример из жизни. Представьте, что вы банк. У ваших клиентов имеются счета. Хранить только информацию об остатке на счете недостаточно. Необходимо также запоминать все транзакции. При использовании шаблона порождения событий остаток на счете будет не просто отдельным полем в базе данных, а значением, рассчитанным на основании сохраненной информации о транзакциях. Это только одно из множества преимуществ, которое предлагает концепция порождения событий.
Данный пакет призван познакомить пользователей фреймворка Laravel с концепцией порождения событий и облегчить ее использование на практике.
Похоже, что Event Projector помогает упростить работу с шаблоном порождения событий. Документация также помогла мне разобраться, каким образом следует использовать этот подход в рамках концепций фреймворка Laravel, с которым я уже знаком.
Основы порождения событий в Laravel
Чтобы разобраться, как работает порождение событий в пакете Event Projector и какие компоненты задействованы в этом процессе, вам следует ознакомиться с разделом Создание первого проектора.
На высоком уровне (простите, если не совсем корректен в своей оценке, поскольку порождение событий — это новая для меня концепция) порождение событий с помощью Event Projector включает в себя:
- Модели Eloquent
- События, реализующие интерфейс ShouldBeStored
- Классы проектора
Пример класса модели со статическим методом createWithAttributes
, приводимый в документации:
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(); } }
Здесь необходимо обратить внимание на события AccountCreated
, MoneyAdded
, MoneySubtracted
и AccountDeleted
, инициируемые соответственно для открытия счета, добавления денег на счет, снятия денег со счета и удаления счета.
Ниже приведен пример события MoneyAdded
(добавление денег на счет). Интерфейс ShouldBeStored указывает пакету Event Projector, что это событие должно быть сохранено:
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; } }
И наконец, частичный пример обработчика событий внутри класса проектора для пополнения средств на счете (мой любимый тип банковского события):
public function onMoneyAdded(MoneyAdded $event) { $account = Account::uuid($event->accountUuid); $account->balance += $event->amount; $account->save(); }
Чтобы связать все это воедино, рассмотрим пример из документации по созданию нового счета и добавлению средств:
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);
Подозреваю, что остаток средств на счетах Люка и Леи выражается в галактических стандартных кредитах, хотя в целом сомневаюсь, что они стали бы открыто вести подобные операции.
Мое внимание также привлекло следующее преимущество порождения событий с использованием этого пакета, указанное в документации:
Интересный момент при работе с проекторами — их можно создавать после того, как события произошли. Представьте, что банк захочет получить отчет о среднем остатке на каждом счете. В таком случае достаточно будет создать новый проектор, воспроизвести все события и получить в итоге эти данные.
Сама идея создания новых проекторов по завершении событий представляется весьма перспективной. Получается, что вам не обязательно получать все «правильные» данные заранее, а можно приходить к результату итеративно.
Что же касается производительности, то согласно документации обращение к проекторам «происходит очень быстро».
Хотите узнать больше?
Для начала я рекомендую ознакомиться с документацией по Event Projector и убедиться, что у вас установлена свежая версия PHP 7.2. Команда Spatie также выложила пример приложения на Laravel, демонстрирующий возможности пакета Event Projector.
Вы можете загрузить код, отметить репозиторий звездочкой, а также внести свой вклад в развитие проекта Event Projector на GitHub. На момент написания статьи над проектом Event Projector работают уже 15 участников, усилиями которых и стал возможен стабильный релиз 1.0. Отличная работа, Spatie! Уверен, что Event Projector станет еще одним великолепным пакетом, который будет по достоинству оценен сообществом Laravel.
ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/460683/
Добавить комментарий