PHPackages                             phpgears/event-sourcing - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. phpgears/event-sourcing

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

phpgears/event-sourcing
=======================

Event Sourcing base

264PHPCI failing

Since Sep 16Pushed 5y ago1 watchersCompare

[ Source](https://github.com/phpgears/event-sourcing)[ Packagist](https://packagist.org/packages/phpgears/event-sourcing)[ RSS](/packages/phpgears-event-sourcing/feed)WikiDiscussions master Synced 3d ago

READMEChangelogDependenciesVersions (2)Used By (0)

[![PHP version](https://camo.githubusercontent.com/d0b5687c6812c5d52d86a548e09db527eeb7860f82adbb677de00a36ddbed1b4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253345253344372e312d3838393242462e7376673f7374796c653d666c61742d737175617265)](http://php.net)[![Latest Version](https://camo.githubusercontent.com/a9b110749029a1e886d07770e4649dd58baa2bf9095f90c3b39c928aa6e47eb7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/phpgears/event-sourcing)[![License](https://camo.githubusercontent.com/ac1ddf606888e903bac693eb6a87de4a2eb5aeb322ea993a54f3d689414c0c1e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://github.com/phpgears/event-sourcing/blob/master/LICENSE)

[![Build Status](https://camo.githubusercontent.com/a5b4abb4c028b6c45683da60264ced1d179eb68ea32b46084703d5f4019696ca/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/phpgears/event-sourcing)[![Style Check](https://camo.githubusercontent.com/cf951d7997f430fe21bb54499987283871589829421f6223e5415745a0a6f6fa/68747470733a2f2f7374796c6563692e696f2f7265706f732f3134393033373533352f736869656c64)](https://styleci.io/repos/149037535)[![Code Quality](https://camo.githubusercontent.com/510edb35e59c192e74d553926e4b6f9434f515201d9f11f573f4fc71814f78a5/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/phpgears/event-sourcing)[![Code Coverage](https://camo.githubusercontent.com/bcd82d02eb3b4b4dafa79d6b5a415f6f3f97cbcb4743e5588691141a67245e0d/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://coveralls.io/github/phpgears/event-sourcing)

[![Total Downloads](https://camo.githubusercontent.com/e874993d9e2697a414c0bdb9740a5ceb4fb108a2ec1294ae9190b6e56a8b2ffe/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/phpgears/event-sourcing/stats)[![Monthly Downloads](https://camo.githubusercontent.com/6622d50803079aa1db505358a12c305406750dc0f45aee7506e9ba23310b2796/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f70687067656172732f6576656e742d736f757263696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/phpgears/event-sourcing/stats)

Event Sourcing
==============

[](#event-sourcing)

Event Sourcing base

Installation
------------

[](#installation)

### Composer

[](#composer)

```
composer require phpgears/event-sourcing

```

Usage
-----

[](#usage)

Require composer autoload file

```
require './vendor/autoload.php';
```

### Aggregate identity

[](#aggregate-identity)

Aggregate identities are provided by [gears/identity](https://github.com/phpgears/identity), head over there to learn about them

### Aggregate events

[](#aggregate-events)

Aggregate Events must implement `Gears\EventSourcing\Event\AggregateEvent` interface so they can be used by Aggregate roots. You can extend from `Gears\EventSourcing\Event\AbstractAggregateEvent` for simplicity

```
use Gears\EventSourcing\Event\AbstractAggregateEvent;
use Gears\Identity\Identity;

class AggregateCreated extends AbstractAggregateEvent
{
    public static function instantiate(Identity $aggregateId)
    {
        return self::occurred($aggregateId, []);
    }
}

/**
 * @method getThing(): string
 */
class SomethingHappened extends AbstractAggregateEvent
{
    public static function hasHappened(Identity $aggregateId, string $whatHappened)
    {
        return self::occurred($aggregateId, ['thing' => $whatHappened]);
    }
}

/**
 * @method getReason(): string
 */
class SomethingFinished extends AbstractAggregateEvent
{
    public static function hasFinished(Identity $aggregateId, string $reason)
    {
        return self::occurred($aggregateId, ['reason' => $reason]);
    }
}
```

Mind that AbstractAggregateEvent constructor is protected forcing you to create static named constructors methods so you can take advantage of type hinting for payload. Although you could use protected method to create aggregate events you're encouraged to use `occurred` method

This events are then recorded and applied on Aggregate root

### Aggregate root

[](#aggregate-root)

Aggregate root should implement `Gears\EventSourcing\Aggregate\AggregateRoot` interface. You can extend from `Gears\EventSourcing\Aggregate\AbstractAggregateRoot` for simplicity

```
use Gears\EventSourcing\Aggregate\AbstractAggregateRoot;
use Gears\Identity\Identity;

class CustomAggregate extends AbstractAggregateRoot
{
    public static function create(Identity $identity): self
    {
        $instance = new self();

        $instance->recordAggregateEvent(AggregateCreated::instantiate($identity));

        return $instance;
    }

    public function doSomething(): void
    {
        $this->recordAggregateEvent(SomethingHappened::hasHappened($this->getIdentity(), 'this happened'));
    }

    public function finishSomething(): void
    {
        $this->recordAggregateEvent(SomethingFinished::hasFinished($this->getIdentity(), 'this finished'));
        $this->recordEvent(WhateverFinished::instance());
    }

    protected function applyAggregateCreated(AggregateCreated $event): void
    {
        $this->setIdentity($event->getAggregateId());
    }

    protected function applySomethingHappened(SomethingHappened $event): void
    {
        // do something with $event->getThing();
    }

    protected function applySomethingFinished(SomethingFinished $event): void
    {
        // do something with $event->getReason();
    }
}
```

#### Aggregate operations

[](#aggregate-operations)

Every operation in aggregates should be made through aggregate events, even aggregate's own creation (see example above), that's the reason AbstractAggregateRoot constructor is protected.

Aggregate events represent facts relevant to the Event Sourcing system such as AggregateCreated, SomethingHappened and SomethingFinished in the previous example

Aggregate events should be finally collected, persisted on an event store and eventually dispatched to an event bus depending on the Projection mechanism at work

```
$aggregateId = CustomAggregateIdentity::fromString('4c4316cb-b48b-44fb-a034-90d789966bac');
$customAggregate = CustomAggregate::create($aggregateId);
$customAggregate->doSomething();

$eventStore->store($customAggregate->collectRecordedAggregateEvents());
```

#### Aggregate Events vs Domain Events

[](#aggregate-events-vs-domain-events)

> This is a key difference between this package and other Event Sourcing libraries out there

Aggregate roots can collect two fundamentally different types of events, Aggregate events have already been discussed in the section above, the second kind of events are Domain events which represent facts relevant to the Domain

They differ drastically and conceptually on who are this events relevant or meant to. While Aggregate Events are meant *only* for the Event Sourcing system, that is Event Store persistence, Aggregate root reconstitution, Projection, Snapshotting, Sagas and others, Domain Events are relevant to the application Domain in itself, that is this and/or other parts or Bounded Contexts of your system

**You should NEVER use Aggregate events to drive your application execution or signal other parts of your system, You MUST use Domain events for those purposes**

Domain events must be collected and sent to an event bus, head to [gears/event](https://github.com/phpgears/event) to learn more about this topic

```
foreach ($customAggregate->collectRecordedEvents() as $domainEvent) {
    /** @var \Gears\Event\EventBus $eventBus */
    $eventBus->dispatch($domainEvent);
}
```

##### Event granularity

[](#event-granularity)

Granularity is key when dealing with this two kinds of events. As a rule of thumb you can consider that Aggregate Events can be mapped to atomic actions that can be performed on an aggregate root, while Domain events can be seen as actions that a user can perform on the domain, they will naturally have a 1:1 relationship in most cases, but not necessarily in all cases

An example that can be of some help to understand this difference is a two step process of user sign up in a system with email validation

While on a first step a user is created in the system (data is fully or partially collected) the user cannot be considered completely registered until he validates his email on a second step

You can consider two methods on the User aggregate to accomplish this task, createUser() that creates the user Aggregate Root with whatever data provided, and validateUser() which validates the user based for example on a code sent to his email

Both of these methods will create an Aggregate Event that will be eventually persisted onto the Event Store, but only the later will create a UserRegistered domain event which can be relevant for other parts of your system (e.g. creating a user wallet on the billing Bounded Context)

### Aggregate Repository

[](#aggregate-repository)

### Event Store

[](#event-store)

#### Concurrency

[](#concurrency)

### Snapshot Store

[](#snapshot-store)

Contributing
------------

[](#contributing)

Found a bug or have a feature request? [Please open a new issue](https://github.com/phpgears/event-sourcing/issues). Have a look at existing issues before.

See file [CONTRIBUTING.md](https://github.com/phpgears/event-sourcing/blob/master/CONTRIBUTING.md)

License
-------

[](#license)

See file [LICENSE](https://github.com/phpgears/event-sourcing/blob/master/LICENSE) included with the source code for a copy of the license terms.

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity11

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/4c50421f1ab4148354dc2dd5dcaba168656b17ea913b310d112deb39a6f73ca1?d=identicon)[juliangut](/maintainers/juliangut)

---

Top Contributors

[![juliangut](https://avatars.githubusercontent.com/u/1104131?v=4)](https://github.com/juliangut "juliangut (43 commits)")

### Embed Badge

![Health badge](/badges/phpgears-event-sourcing/health.svg)

```
[![Health](https://phpackages.com/badges/phpgears-event-sourcing/health.svg)](https://phpackages.com/packages/phpgears-event-sourcing)
```

###  Alternatives

[ttree/contentrepositoryimporter

Helper package to import data in the Neos content repository

1510.2k](/packages/ttree-contentrepositoryimporter)

PHPackages © 2026

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