事件源基础

安装: 16

依赖: 0

建议者: 0

安全性: 0

星级: 0

关注者: 1

分支: 0

开放问题: 0

类型:基础

0.1.8 2018-06-14 11:47 UTC

This package is not auto-updated.

Last update: 2024-09-29 05:55:17 UTC


README

安装

使用Composer - 执行

composer require  brueggemann/esf

设置

在bootstrap/app.php中注册ESFoundationServiceProvider

$app->register(ESFoundation\ServiceProviders\ESFoundationServiceProvider::class);

对于ESF外观取消注释

$app->withFacades();

对于Redis需要RedisServiceProvider

$app->register(\Illuminate\Redis\RedisServiceProvider::class);

并加载正确的配置

$app->configure('database');

在config/database.php中需要类似以下的配置

'redis' => [

        'client' => 'predis',

        'default' => [
            'host' => env('REDIS_HOST', '10.0.2.2'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ],
        'events' => [
            'host' => env('REDIS_HOST', '10.0.2.2'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 1,
        ],
        'aggregates' => [
            'host' => env('REDIS_HOST', '10.0.2.2'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 2,
        ],
        'queries' => [
            'host' => env('REDIS_HOST', '10.0.2.2'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 3,
        ],
    ],

.env

QueryRepository、EventStore、AggregateProjectionRepository和EventBus都有内存和redis实现。

QUERY_REPOSITORY=redis# 
EVENT_STORE=redis
AGGREGATE_REPOSITORY=redis
EVENT_BUS=memory
COMMAND_BUS=memory

Artisan

有几个Artisan命令可以帮助创建所需的基类。

$ php artisan make:aggregateRoot
$ php artisan make:aggregateRootProjection
$ php artisan make:aggregateRootValidator
$ php artisan make:command
$ php artisan make:commandHandler
$ php artisan make:event

用法

命令

您应用程序事件源部分的入口应该是CommandHandler。

$ php artisan make:commandHandler -name NAME -command COMMANDNAME -command COMMANDNAME -eventBus

如果CommandHandler需要AggregateProjectionRepository,请添加

$ [...] -aggregateProjectionRepository

如果使用CommandBus,则需要在AppServiceProvider中注册CommandHandler

public function register()
    {
        $commandBus = ESF::commandBus();
        $commandBus->subscribe(app(ShippingCommandHandler::class));
        
        [...]
    }

在创建的CommandHandler的handleCOMMAND方法中,可以通过以下方式加载AggregateProjection

AGGREGATEROOT::load(AggregateRootId::new($AGGREGATEROOTID));

或如果aggregateProjectionRepository在构造函数中注入

$this->aggregateProjectionRepository->load(new AggregateRootId($AGGREGATEROOTID), AGGREGATEROOT::class);

事件可以通过以下方式存储

ESF::eventBus()->dispatch($DOMAINEVENTSTREAM);

或如果eventStore在构造函数中注入

$this->eventBus->dispatch($DOMAINEVENTSTREAM);

在创建的Command类中,可以使用标准的Laravel验证方式定义规则

public function rules()
{
    return [
        'foo' => 'required|string'
    ];
}

Command的构造函数接受任何形式的负载,并将其与定义的rules()方法的规则进行验证。

  • 如果没有定义规则,则接受任何负载。
  • 如果负载中的键未在规则中定义,则将其丢弃。
  • 如果负载中的值未通过验证,则抛出异常。

最好是定义一个命名的数组作为负载

new COMMAND([
  'foo' => 'bar
]);

可以获取整个负载

$COMMAND->getPayload();

或按键获取

$COMMAND->foo;

CommandHandler可以直接调用

$commandHandler = app(COMMANDHANDLER::class);
$commandHandler->handle(new COMMAND(['foo => 'bar]));

或使用COMMANDBUS

ESF::commandBus()->dispatch(new COMMAND(['foo' => 'bar']));

聚合

聚合分为三个部分

  • 逻辑:AggregateRoot
  • 表示:AggregateRootProjection
  • 验证:AggregateRootValidator
$ php artisan make:aggregateRoot -name NAME -event EVENTNAME -event EVENTNAME -projection

如果AggregateRoot需要Validator,请添加

$ [...] -validator

在创建的AggregateRoot类的applyThatEVENT方法中,可以将$EVENT中的数据应用于$AGGREGATEROOTPROJECTION

$AGGREGATEROOTPROJECTION->foo = $EVENT->bar;
return true;

创建的事件类类似于Command类。可以定义规则,以确定事件负载的验证。

如果类似的规则对命令和事件都有效,则建议使用ValueObject来表示规则和每个实例的值。

$ php artisan make:valueObject -name NAME

然后可以将这些规则导入到COMMAND或/和EVENT中

    public function rules()
    {
        return [
            'foo' => VALUEOBJECT::rules(),
        ];
    }

如果创建了AggregateRootValidator类,则可以通过在AggregateProjection未满足给定要求时返回false来防止Event应用于AggregateRootProjection。当验证失败时,会抛出FailedValidation异常。

创建的AggregateRootProjection代表聚合的状态。它包含多个实例化的ValueObjects。

    public static function valueObjects(): Collection
    {
        return collect([
            'foo' => Foo::class,
            'foo2'=> Foo::class
        ]);
    }

要使用AggregateRoot的逻辑将事件应用于AggregateRootProjection,有多种选择

AGGREGATEROOT::applyOn($AGGREGATEPROJECTION)->that($EVENT); // 1

AGGREGATEROOT::applyThat($EVENT, $AGGREGATEROOTPROJECTION); // 2

$AGGREGATEROOTPROJECTION->applyThat($EVENT);                // 3

在1和2中可以选择AggregateRoot的逻辑,而在3中使用默认逻辑。

将事件应用于AggregateRootProjection的每个事件都会保存并可以检索

$events = $AGGREGATEROOTPROJECTION->popUncommittedEvents();

然后可以通过EventBus或如果需要,直接通过EventStore提交这些事件

ESF::eventBus()->dispatch($events);
ESF::eventStore()->push($events);

查询

由于没有检索事件或甚至聚合的特定模式的方法,例如SQL中的where子句,因此数据累积是通过查询完成的

查询应表示一个页面或视图。为了管理查询,需要使用查询存储库(QueryRepository)。

$queryRepository = ESF::queryReporitory();

添加方法保存键值对。如果值被更新,则在同一键下插入一个新条目。

$queryRepository->add('foo', 'bar');

要检索查询,使用get方法。如果只提供键,则返回最新的查询。如果还提供了一个索引,则返回对应的较旧查询。

$queryRepository->get('foo'); //bar
$queryRepository->get('foo', 0); //bar

查询存储库可用于保存长期计算、爬虫或仅间接持久化的数据。

事件监听器

事件监听器是监听器模式的基本实现。任何事件监听器都需要订阅它打算监听的事件总线(EventBus)。在AppServiceProvider中进行此操作是一个不错的选择。

$eventBus = ESF::eventBus();
$eventBus->subscribe(app(EVENTLISTENER::class));

如果需要同步处理事件数据,但在聚合根(AggregateRoot)中无法完成,这是一个创建和更新查询的好地方。