PHPackages                             gember/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. gember/event-sourcing

ActiveLibrary

gember/event-sourcing
=====================

Use case driven EventSourcing - Let go of the Aggregate with the Dynamic Consistency Boundary (DCB) pattern.

0.13.1(3mo ago)82.5k6MITPHPPHP ^8.3CI passing

Since Apr 27Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/GemberPHP/event-sourcing)[ Packagist](https://packagist.org/packages/gember/event-sourcing)[ RSS](/packages/gember-event-sourcing/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (11)Versions (15)Used By (6)

🫚 Gember Event sourcing
=======================

[](#-gember-event-sourcing)

[![Build Status](https://camo.githubusercontent.com/d357fc3ce0718a166f8d9ffb05f93fe107c74f0135d8fca6ac2c86608accd0c1/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f47656d6265725048502f6576656e742d736f757263696e672f6261646765732f6275696c642e706e673f623d6d61696e)](https://github.com/GemberPHP/event-sourcing/actions)[![Coverage Status](https://camo.githubusercontent.com/f48130606615fbb9289dd8a053bf265e321036ea29ea9ae39105f7db30cfe3a2/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f636f7665726167652f672f47656d6265725048502f6576656e742d736f757263696e672e7376673f7374796c653d666c6174)](https://scrutinizer-ci.com/g/GemberPHP/event-sourcing/code-structure)[![Quality Score](https://camo.githubusercontent.com/72fa4c894e280845d6c34178d2a3bbb6d490b135db84c152be736a98df2d9169/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f47656d6265725048502f6576656e742d736f757263696e672e7376673f7374796c653d666c6174)](https://scrutinizer-ci.com/g/GemberPHP/event-sourcing)[![Software License](https://camo.githubusercontent.com/f251623e510f5909f16ae3f4e6e548dac11340b9fde1a99be26b015b39272c00/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c6174)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/05de8fa997dec7bd6b39b7af8c030c734835c6871547f8b2d9ae87e4299ebc8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e332d3838393242462e7376673f7374796c653d666c6174)](http://www.php.net)

*Use case driven EventSourcing - Let go of the Aggregate with the Dynamic Consistency Boundary (DCB) pattern.*

Documentation
-------------

[](#documentation)

- [Background](/docs/background.md)
- [Installation](/docs/installation.md)
- [Usage](/docs/usage.md)
    - [Commands](/docs/usage/commands.md) - Define commands that carry intent and domain tags for event retrieval
    - [Use cases / aggregates](/docs/usage/use-cases.md) - Model business logic using event-sourced use cases and traditional aggregates with DCB or aggregate patterns
    - [Command handlers](/docs/usage/command-handlers.md) - Trigger behavioral actions on use cases using command handlers
    - [Domain events](/docs/usage/domain-events.md) - Define and work with domain events, including naming, serialization, and domain tags
    - [Sagas](/docs/usage/sagas.md) - Implement long-running business processes that coordinate complex workflows across multiple domain events
- [How it works](/docs/how-it-works.md) - End-to-end flow, event store structure, CQRS, and the read side
- [Library architecture](/docs/library-architecture.md) - Internal code organization, design patterns, resolver and registry layers

In a nutshell
-------------

[](#in-a-nutshell)

#### Traditional 'Aggregate driven' EventSourcing

[](#traditional-aggregate-driven-eventsourcing)

Domain concepts are modeled towards objects: the aggregate.

- Any business logic related to a single domain object should live inside the aggregate
- Logic that involves other domain objects or groups of the same kind of domain objects does not belong in the aggregate

[![aggregate-driven-event-sourcing](/docs/images/aggregate-driven-event-sourcing.png)](/docs/images/aggregate-driven-event-sourcing.png)

#### 'Use case driven' EventSourcing

[](#use-case-driven-eventsourcing)

Domain concepts are modeled through use cases.

- Any business logic tied to a use case should live inside that use case
- A use case can relate to one or more domain concepts

[![use-case-driven-event-sourcing](/docs/images/use-case-driven-event-sourcing.png)](/docs/images/use-case-driven-event-sourcing.png)

A simple example
----------------

[](#a-simple-example)

This example demonstrates all key features of the library: a DCB use case with command handler, domain events, and a saga coordinating a workflow.

**Scenario**: A student subscribes to a course. When subscription succeeds, a saga automatically sends a welcome email.

### Domain Events

[](#domain-events)

```
use Gember\EventSourcing\UseCase\Attribute\DomainEvent;
use Gember\EventSourcing\UseCase\Attribute\DomainTag;
use Gember\EventSourcing\Saga\Attribute\SagaId;

#[DomainEvent(name: 'course.created')]
final readonly class CourseCreatedEvent
{
    public function __construct(
        #[DomainTag]
        public string $courseId,
        public string $name,
    ) {}
}

#[DomainEvent(name: 'student.registered')]
final readonly class StudentRegisteredEvent
{
    public function __construct(
        #[DomainTag]
        public string $studentId,
        public string $email,
    ) {}
}

#[DomainEvent(name: 'student.subscribed')]
final readonly class StudentSubscribedEvent
{
    public function __construct(
        #[DomainTag]
        #[SagaId]  // Links to SubscriptionWelcomeSaga
        public string $courseId,
        #[DomainTag]
        #[SagaId]
        public string $studentId,
    ) {}
}
```

### Use Case with Command Handler

[](#use-case-with-command-handler)

```
use Gember\EventSourcing\Common\CreationPolicy;
use Gember\EventSourcing\UseCase\Attribute\DomainCommandHandler;
use Gember\EventSourcing\UseCase\Attribute\DomainEventSubscriber;
use Gember\EventSourcing\UseCase\Attribute\DomainTag;
use Gember\EventSourcing\UseCase\EventSourcedUseCase;
use Gember\EventSourcing\UseCase\EventSourcedUseCaseBehaviorTrait;

final class SubscribeStudentToCourse implements EventSourcedUseCase
{
    use EventSourcedUseCaseBehaviorTrait;

    #[DomainTag]
    private CourseId $courseId;

    #[DomainTag]
    private StudentId $studentId;

    private bool $isSubscribed = false;

    /**
     * Subscribes a student to a course (DCB pattern with multiple domain tags).
     * Uses __invoke to emphasize this is a single-purpose use case.
     */
    #[DomainCommandHandler(policy: CreationPolicy::IfMissing)]
    public function __invoke(SubscribeStudentCommand $command): void
    {
        // 1. Check idempotency
        if ($this->isSubscribed) {
            return;
        }

        // 2. Protect invariants (simplified for example)
        // In real scenarios: check capacity, prerequisites, etc.

        // 3. Apply domain event
        $this->apply(new StudentSubscribedEvent(
            $command->courseId,
            $command->studentId,
        ));
    }

    #[DomainEventSubscriber]
    private function onCourseCreated(CourseCreatedEvent $event): void
    {
        $this->courseId = new CourseId($event->courseId);
    }

    #[DomainEventSubscriber]
    private function onStudentRegistered(StudentRegisteredEvent $event): void
    {
        $this->studentId = new StudentId($event->studentId);
    }

    #[DomainEventSubscriber]
    private function onStudentSubscribed(StudentSubscribedEvent $event): void
    {
        $this->isSubscribed = true;
    }
}
```

### Saga

[](#saga)

```
use Gember\DependencyContracts\Util\Messaging\MessageBus\CommandBus;
use Gember\EventSourcing\Common\CreationPolicy;
use Gember\EventSourcing\Saga\Attribute\Saga;
use Gember\EventSourcing\Saga\Attribute\SagaEventSubscriber;
use Gember\EventSourcing\Saga\Attribute\SagaId;

#[Saga(name: 'subscription.welcome')]
final class SubscriptionWelcomeSaga
{
    #[SagaId]
    public ?string $courseId = null;

    #[SagaId]
    public ?string $studentId = null;

    private bool $welcomeEmailSent = false;

    /**
     * When a student subscribes, automatically send a welcome email.
     */
    #[SagaEventSubscriber(policy: CreationPolicy::IfMissing)]
    public function onStudentSubscribed(StudentSubscribedEvent $event, CommandBus $commandBus): void
    {
        $this->courseId = $event->courseId;
        $this->studentId = $event->studentId;

        // Dispatch command to send welcome email
        $commandBus->handle(new SendWelcomeEmailCommand(
            $event->studentId,
            $event->courseId,
        ));

        $this->welcomeEmailSent = true;
    }
}
```

For more extended examples and complete implementations, check out the demo application [gember/example-event-sourcing-dcb](https://github.com/GemberPHP/example-event-sourcing-dcb).

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance80

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 93.5% 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 ~21 days

Recently: every ~28 days

Total

14

Last Release

108d ago

### Community

Maintainers

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

---

Top Contributors

[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (43 commits)")[![jerowork](https://avatars.githubusercontent.com/u/4119451?v=4)](https://github.com/jerowork "jerowork (3 commits)")

---

Tags

cqrsdcbddddomain-driven-designdynamic-consistency-boundaryevent-sourcinggemberDomain Driven Designdddevent sourcingcqrsdcbgemberdynamic-consistency-boundary

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[broadway/broadway

Infrastructure and testing helpers for creating CQRS and event sourced applications.

1.5k2.0M53](/packages/broadway-broadway)[broadway/broadway-bundle

Symfony bundle for broadway/broadway.

68853.8k7](/packages/broadway-broadway-bundle)[nwidart/laravel-broadway

A Laravel adapter for the Broadway ES/CQRS package.

12315.0k](/packages/nwidart-laravel-broadway)

PHPackages © 2026

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