Installation and First Steps

Installation

composer require ecotone/pdo-event-sourcing

And go to DBAL Support in order to configure the connection.

To get started quickly you may find Demo Project useful.

Ecotone Event Sourcing is based on well known and stable Prooph's Event Store. It does provide support for three databases: - PostgreSQL - MySQL - MariaDB

Storing Events in streams using Aggregates

There are two ways, we can make use of Event Sourced Aggregates.

Pure Event Sourced Aggregate

This way of handling events does allow for pure functions. Writes are clearly separated from writes.

#[EventSourcingAggregate] // 1
class Ticket
{
    use WithAggregateVersioning; // 2

    #[Identifier] // 1
    private string $ticketId;
    private string $ticketType;

    #[CommandHandler] // 2
    public static function register(RegisterTicket $command) : array
    {
        return [new TicketWasRegistered($command->getTicketId(), $command->getTicketType())];
    }

    #[CommandHandler] // 2
    public function close(CloseTicket $command) : array
    {
        return [new TicketWasClosed($this->ticketId)];
    }

    #[EventSourcingHandler] // 4
    public function applyTicketWasRegistered(TicketWasRegistered $event) : void
    {
        $this->ticketId       = $event->getTicketId();
        $this->ticketType     = $event->getTicketType();
    }
}
  1. EventSourcingAggregate and Identifier works exactly the same as State-Stored Aggregate.

  2. Event Sourced Aggregate must provide version. You may leave it to Ecotone using WithAggregateVersioning or you can implement it yourself.

  3. CommandHandlerfor event sourcing returns events generated by specific method. This will be passed to the Repository to be stored.

  4. EventSourcingHandler is method responsible for reconstructing Aggregate from previously created events. At least one event need to be handled in order to provide Identifier.

Internal Recorder Aggregate

This way of handling events allow for similarity with State Stored Aggregates.

#[EventSourcingAggregate(true)] // 1
class Basket
{
    use WithAggregateEvents;
    use WithAggregateVersioning;

    #[Identifier]
    private string $id;

    #[CommandHandler] // 2
    public static function create(CreateBasket $command) : static
    {
        $basket = new static();
        $basket->recordThat(new BasketWasCreated($command->getId()));

        return $basket;
    }

    #[CommandHandler] // 2
    public function addProduct(AddProduct $command) : void
    {
        $this->recordThat(new ProductWasAddedToBasket($this->id, $command->getProductName()));
    }

    #[EventSourcingHandler]
    public function applyBasketWasCreated(BasketWasCreated $basketWasCreated)
    {
        $this->id = $basketWasCreated->getId();
    }
}
  1. In order to make use of alternative way of handling events, we need to set attribute to trueEventSourcingAggregate(true)

  2. Command Handlers instead of returning events are acting the same as State Stored Aggregates. All events which will be published using recordThatwill be passed to the Repository to be stored.

Handling Event Sourced Saga

Just like Aggregates can be stored in form of events using Event Sourcing Aggregates, the same is for Sagas.

#[EventSourcingSaga]
class OrderFulfillment
(...)

Last updated