/EventSauce

This is work in progress, not stable, very experimental etc.

Primary LanguagePHP

EventSaucePHP

Scrutinizer Code Quality Code Coverage Build Status

EventSauce is a somewhat opinionated, no-nonsense, and easy way to introduce event sourcing into PHP projects. It's designed so storage and queueing mechanisms can be chosen based on your specific requirements. It has test tooling, designed to work with an event sourcing mindset.

That's it.

Examples

Have an AggregateRoot

<?php

use EventSauce\EventSourcing\BaseAggregateRoot;
use EventSauce\EventSourcing\Time\Clock;

final class EventSourcingThing extends BaseAggregateRoot
{
    private $name;

    public function rename(Clock $clock, string $newName)
    {
        if ($this->name !== $newName) {
            $this->recordThat(new TheThingWasRenamed(
                $this->aggregateRootId(),
                $clock->pointInTime(),
                $newName
            ));
        }
    }
    
    protected function applyTheThingWasRenamed(TheThingWasRenamed $event)
    {
        $this->name = $event->name();
    }
}

and test it.

<?php

use EventSauce\EventSourcing\AggregateRootTestCase;
use EventSauce\EventSourcing\AggregateRootRepository;
use EventSauce\EventSourcing\AggregateRootId;
use EventSauce\EventSourcing\UuidAggregateRootId;
use EventSauce\EventSourcing\CommandHandler;
use EventSauce\EventSourcing\Time\Clock;

class EventSourcingThingTest extends AggregateRootTestCase
{
    protected function aggregateRootId(): AggregateRootId
    {
        return UuidAggregateRootId::create();
    }
    
    protected function aggregateRootClassName(): string
    {
        return EventSourcingThing::class;
    }
    
    protected function commandHandler(AggregateRootRepository $repository, Clock $clock): CommandHandler
    {
        return new SomeCommandHandler($repository, $clock);
    }
    
    /**
     * @test
     */
    public function renaming()
    {
        $aggregateRootId = new AggregateRootId('identifier');
        $this->when(new RenameTheThing($aggregateRootId, 'new name'))
            ->then(new TheThingWasRenamed(
                $aggregateRootId,
                $this->pointInTime(),
                'new name'
            ));
    }
}

Concepts

AggregateRoot

The AggregateRoot is the main Entity which is our contact point to the internal behavior/process.

AggregateRootRepository

The AggregateRootRepository is our main point of infrastructural contact,

Event

An event represents something that happened which is relevant to the business.

Message

A message is the distributed format of an event which can contain relevant, not domain specific, metadata.

MessageRepository

The message is responsible for persisting and retrieving Message's. A MessageRepository is like an EventStore, but contains messages instead of purely events.

MessageDispatcher

The MessageDispatcher is responsible for dispatching messages to Consumer's.

Consumer

A Consumer handles Messages's.

ProcessManager?

This is just a Consumer.

Projector?

This is just a Consumer.