Different ways to Record Events

Two ways of setting up Event Sourced Aggregates

There are two ways we can configure our Aggregate to record Events.

1) Pure Event Sourced Aggregate

This way of handling events does allow for pure functions. This means that actions called on the Aggregate returns Events and are not changing internal state of Aggregate.

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

2) Internal Recorder Aggregate

This way of handling events allow for similarity with State Stored Aggregates. This convention requires changing internal state of Aggregate to record Events. Therefore Pure ES Aggregate is recommended as it's not require for any internal state changes in most of the scenarios. However ES Aggregate with Internal Recorder may be useful for projects migrating with other solutions, or when our team is heavily used to working this way.

#[EventSourcingAggregate] 
class Basket
{
    use WithEvents; // 1
    use WithVersioning;

    #[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 provide trait WithEvents.

  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.

Last updated