定制 brueggemann/esf 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

brueggemann/esf

Composer 安装命令:

composer require brueggemann/esf

包简介

Event Sourcing Foundation

README 文档

README

Installing :

Using Composer - execute

composer require  brueggemann/esf

Setup :

Register the ESFoundationServiceProvider in the bootstrap/app.php

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

For the ESF Facade uncomment:

$app->withFacades();

For Redis the RedisServiceProvider is needed:

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

and the correct configuration has to be loaded:

$app->configure('database');

In config/database.php a configuration similar to this is needed:

'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

The QueryRepository, EventStore, AggregateProjectionRepository and EventBus have both in memory and redis implementations.

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

Artisan :

There are a few Artisan Commands to help create the basic classes needed.

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

Usage :

Commands :

The entrypoint to the Event Sourced part of your application should be a CommandHandler.

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

If an AggregateProjectionRepository is needed for the CommandHandler add

$ [...] -aggregateProjectionRepository

If a CommandBus is in use, the CommandHandler has to be registered in the AppServiceProvider:

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

In the handleCOMMAND methods of the created CommandHandler an AggregateProjection can be loaded either by:

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

or if the aggregateProjectionRepository is injected in the constructor

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

Events can be either stored with:

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

or if the eventStore is injected in the constructor

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

In the created Command classes rules may be defined in the standart laravel validation way:

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

A Commands constructor takes any form of payload and validates it against the requirements of the defined rules() method.

  • If no rules are defined any payload is acceped.
  • If keys in the payload are not defined in the rules they are thrown out.
  • If values in the payload are not passing validation an Exception is thrown.

Best is to define a named array as payload:

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

It is possible to either retrieve the payload as a whole:

$COMMAND->getPayload();

or by key:

$COMMAND->foo;

A CommandHandler is either called direcly:

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

or using the COMMANDBUS:

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

Aggregates :

Aggregates are split into three parts:

  • logic: AggregateRoot
  • representation: AggregateRootProjection
  • validation: AggregateRootValidator
$ php artisan make:aggregateRoot -name NAME -event EVENTNAME -event EVENTNAME -projection

If a Validator is needed for the AggregateRoot add:

$ [...] -validator

In the applyThatEVENT methods of the created AggregateRoot class the data from the $EVENT can be applied to the $AGGREGATEROOTPROJECTION

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

The created Event class works similar to the Command class. Rules may be defined, that determine the events payload validation.

If similar Rules are valid both for a Command and an Event it is recommended to use a ValueObject to represent both the rules and a value per instance.

$ php artisan make:valueObject -name NAME

These rules then can be imported into the COMMAND or/and EVENT:

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

If created the AggregateRootValidator class can prevent an Event on being applied to an AggregateRootProjection by returning false when a given requirement of the AggregateProjection is not fulfilled. When the validation fails a FailedValidation exception is thrown.

The created AggregateRootProjection represents a state of an Aggregate. It contains multiple instantiated ValueObjects.

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

To apply an Event to an AggregateRootProjection using the AggregateRoots logic there are multiple options:

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

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

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

In 1 and 2 it is possible to choose the AggregateRoots logic, where in 3 the default logic is used.

Every Event that is applied to an AggregateRootProjection is saved and can be retrieved

$events = $AGGREGATEROOTPROJECTION->popUncommittedEvents();

These Events then are commitable via the EventBus or if needed directly via the EventStore

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

Queries :

Since there are no means of retrieving Events or even Aggregates based on a specific pattern, like the where clause in SQL the accumulation of data is done via Queries

A Query should represent a page or view. To manage Queries the QueryRepository is needed.

$queryRepository = ESF::queryReporitory();

The add method saves a key-value pair. If the value is updated, a new entry under the same key is inserted.

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

To retrieve a query the get method is used. If only the key is provided, the most recent query is returned. If additionally an index is provided, the corresponding older query is returned.

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

The QueryRepository can be used to save data of long lasting calculations, crawlers or only indirectly persisted data.

EventListener :

The EventListener is a basic implementation of the listener pattern. Any EventListener needs to be subscribed to the EventBus it is supposed to be listening on. A good place to do this is in the AppServiceProvider:

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

If synchronus handling of eventdata is required, but cannot be done in an AggregateRoot, this is a good place to create and update a query.

brueggemann/esf 适用场景与选型建议

brueggemann/esf 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 18 次下载、GitHub Stars 达 0, 最近一次更新时间为 2018 年 06 月 08 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 brueggemann/esf 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 brueggemann/esf 我们能提供哪些服务?
定制开发 / 二次开发

基于 brueggemann/esf 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 18
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 7
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2018-06-08