twanhaverkamp/event-sourcing-for-php

1.1.0-beta 2023-08-07 17:55 UTC

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

链接和参考