twanhaverkamp / event-sourcing-for-php
PHP的事件源库
1.1.0-beta
2023-08-07 17:55 UTC
Requires
- php: ^8.2
- symfony/uid: ^6.3
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.2
- squizlabs/php_codesniffer: ^3.7
This package is auto-updated.
Last update: 2024-09-07 20:12:30 UTC
README
PHP的事件源库。
安装
此库作为 Composer 包提供
composer require twanhaverkamp/event-sourcing-for-php
要求
- PHP 8.2
注意:请仔细查看示例类。这些类可以在 /Example 目录中找到。
组件
聚合根
聚合根类实现 AggregateRootInterface
(或扩展 AbstractAggregateRoot
)并记录所有事件。通常对象通过魔法方法 __construct
实例化,但由于这本身就是一个事件,因此通过静态方法(最好描述其上下文中的事件)来完成。
构造函数实例化
<?php namespace MyApp\Entity; final class User { public function __construct( private string $emailAddress, private string $hashedPassword, ) { } public function setPassword(string $hashedPassword): void { $this->hashedPassword = $hashedPassword; } } // ... use MyApp\Entity\User; $user = new User('[email_address]', '[hashed_password]'); $user->setPassword('[new_hashed_password]');
静态方法实例化
<?php namespace MyApp\AggregateRoot; use MyApp\Event\UserWasRegisteredEvent; use TwanHaverkamp\EventSourcingForPhp\AggregateRoot\AbstractAggregateRoot; use TwanHaverkamp\EventSourcingForPhp\Uuid\Uuid; final class User extends AbstractAggregateRoot { public static function register(string $emailAddress, string $hashedPassword): self { $aggregateRoot = new self($aggregateRootId = Uuid::init()); $aggregateRoot->recordEvent(new UserWasRegisteredEvent( $emailAddress, $hashedPassword, $aggregateRootId, )); return $aggregateRoot; } public function changePassword(string $newHashedPassword): void { $this->recordEvent(new UserPasswordWasChanged( $newHashedPassword, $this->getId(), )); } } // ... use MyApp\AggregateRoot\User; $user = User::register('[email_address]', '[hashed_password]'); $user->changePassword('[new_hashed_password]');
当聚合根通过其事件存储(在对象的 getEventStore
方法中定义)保存时,可以使用 getProjection
方法将其转换为投影(或数据传输对象)
<?php use MyApp\EventStore\MyEventStore; // ... $this->myEventStore->save($user); /** @var object $projection */ $projection = $user->getProjection();
使用静态 reconstitute
方法,可以使用事件存储中的事件重新构建聚合根。
事件
事件类实现 EventInterface
(或扩展 AbstractEvent
)并保存聚合根的事件相关数据。最好将事件描述成过去发生的事情,如:[主体] + [动词] + [动作] + [类后缀]。用户注册变成 UserWasRegisteredEvent
。
重要的事件方法
getAggregateRootId
:这建立了与相关聚合根的关系。getRecordedAt
:这确定了聚合根事件发生的顺序。getPayload
:这包含影响聚合根的所有相关数据。
<?php namespace MyApp\Event; use TwanHaverkamp\EventSourcingForPhp\Event\AbstractEvent; use TwanHaverkamp\EventSourcingForPhp\Uuid\UuidInterface; final class UserWasRegisteredEvent extends AbstractEvent { public function __construct( private readonly string $emailAddress, private readonly string $hashedPassword, private readonly UuidInterface $aggregateRootId, ) { parent::__construct($aggregateRootId, new \DateTimeImmutable()); } public function getPayload(): array{ return [ 'emailAddress' => $this->emailAddress, 'hashedPassword' => $this->hashedPassword, ]; } }
事件存储
事件存储类实现 EventStoreInterface
(或扩展 AbstractEventStore
)并负责管理聚合根的事件。
如果一个项目有多个事件存储,可以使用 EventStoreManager
。使用此类,在加载或保存聚合根时使用正确的事件存储。
注意:事件被认为是“只读”的,应该保存到持久存储中。
GitHub Actions
快速测试
- Composer 审计
- PHPUnit(单元测试套件)
- PHPStan
- PHP CodeSniffer
链接和参考
- 事件源 由 Martin Fowler 解释
- 领域驱动设计 一本由 Eric Evans 编写的书
- 实现领域驱动设计 一本由 Vaughn Vernon 编写的书
- Frank de Jonge 的 EventSauce 项目
- Broadway 项目
- Refactoring.Guru:设计模式