PHPackages                             brueggemann/esf - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. brueggemann/esf

ActiveFoundation

brueggemann/esf
===============

Event Sourcing Foundation

0.1.8(7y ago)018MITPHPPHP &gt;=7.1.3

Since Jun 8Pushed 7y agoCompare

[ Source](https://github.com/VBrueggemann/ESF)[ Packagist](https://packagist.org/packages/brueggemann/esf)[ RSS](/packages/brueggemann-esf/feed)WikiDiscussions master Synced today

READMEChangelog (10)Dependencies (9)Versions (16)Used By (0)

EventSourcingFoundation
=======================

[](#eventsourcingfoundation)

Installing :
============

[](#installing-)

Using Composer - execute

```
composer require  brueggemann/esf

```

Setup :
=======

[](#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
====

[](#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 :
=========

[](#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 :
=======

[](#usage-)

Commands :
----------

[](#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-)

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 :
---------

[](#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 :
---------------

[](#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.

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

15

Last Release

2888d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/eac22a032f7035a34e9e8e5bb82907692c6110bbd63bced771005bc882f613d8?d=identicon)[VBrueggemann](/maintainers/VBrueggemann)

---

Top Contributors

[![VBrueggemann](https://avatars.githubusercontent.com/u/30142828?v=4)](https://github.com/VBrueggemann "VBrueggemann (32 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/brueggemann-esf/health.svg)

```
[![Health](https://phpackages.com/badges/brueggemann-esf/health.svg)](https://phpackages.com/packages/brueggemann-esf)
```

###  Alternatives

[grumpydictator/firefly-iii

Firefly III: a personal finances manager.

22.8k69.3k](/packages/grumpydictator-firefly-iii)[themosis/framework

The Themosis framework.

676307.9k18](/packages/themosis-framework)[temporal/sdk

Temporal SDK

4002.2M18](/packages/temporal-sdk)[vonage/jwt

A standalone package for creating JWTs for Vonage APIs

424.1M4](/packages/vonage-jwt)[pdffiller/qless-php

PHP Bindings for qless

29113.2k1](/packages/pdffiller-qless-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
