prooph/event-store-symfony-bundle

How to use SingleStreamStrategy?

enumag opened this issue · 6 comments

I'm trying to enable SingleStreamStrategy to fix performance issues in projections (see proophsoftware/es-emergency-call#5 for details).

For now I'm unable to get it working. EventStore still makes one stream per aggregate instead of per aggregate type. Here is my current configuration:

services:
    Prooph\EventStore\Pdo\PostgresEventStore:
        arguments: ['@messageFactory', '@eventStoreConnection', '@stream_strategy']

    stream_strategy:
        class: Prooph\EventStore\Pdo\PersistenceStrategy\PostgresSingleStreamStrategy

In the documentation I found a link to this example to initialize the streams. This creates one more stream in the database but the EventStore is still creating new streams for every new aggregate. Also the example is confusing. Shouldn't I create one stream for each aggregate type instead of just one stream?

Ok it seems it needs to be configured on each repository definition as well:

prooph_event_store:
    stores:
        acme_store:
            event_store: Prooph\EventStore\Pdo\MysqlEventStore
            repositories:
                todo_list:
                    repository_class: Prooph\ProophessorDo\Infrastructure\Repository\EventStoreUserCollection
                    aggregate_type: Prooph\ProophessorDo\Model\User\User
                    aggregate_translator: prooph_event_sourcing.aggregate_translator
                    # I was missing this line.
                    one_stream_per_aggregate: false

Now however eventstore only uses that one stream I created based on the example script linked above. All events from all aggregate types are mixed in this stream. How can I separate them to one stream per aggregate type?

Ok more progress. Looks like I need to override AggregateRepository::determineEventStream with this:

	protected function determineStreamName(string $aggregateId): StreamName
	{
		return new StreamName($this->aggregateType->toString());
	}

Now I need to correctly initialize the event streams which means I need to somehow ask prooph for names of all aggregate types that exist throughout the application. How can I do that?

@enumag You don't need to overwrite AggregateRepository::determineEventStream.
It should be enough to set the stream_name in the event-store configuration.

prooph_event_store:
    stores:
        acme_store:
            event_store: Prooph\EventStore\Pdo\MysqlEventStore
            repositories:
                todo_list:
                    repository_class: Prooph\ProophessorDo\Infrastructure\Repository\EventStoreUserCollection
                    aggregate_type: Prooph\ProophessorDo\Model\User\User
                    aggregate_translator: prooph_event_sourcing.aggregate_translator
                    one_stream_per_aggregate: false
                    # Set the stream name
                    stream_name: "what-ever-you-want"

Initializing of the stream is – in my setup – currently part of the database migration. Automation of this sounds tricky. But of course you could get the config of the event store bundle, grab all repositories, filter to get just the one with one stream per aggregate type and create them. I'm just not sure whether this is worth the trouble …

@UFOMelkor I didn't realize I need to put that to database migrations. Thanks for pointing it out.

Well this pretty much resolves this issue for me. But it is probably a good idea to add some notes to the documentation about how to setup the SingleStreamStrategy. As you can see I've spent some time to get it working so it's not exactly clear.

Definitely – I will let the issue open for the documentation 👍

There is a command by @gquemener that comes with the symfony recipe:

You can adapt it to your needs e.g.:

use Prooph\EventStore\EventStore;
use Prooph\EventStore\Stream;
use Prooph\EventStore\StreamName;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class CreateEventStreamCommand extends Command
{
    protected static $defaultName = 'acme:create-event-streams';

    /** @var EventStore $eventStore */
    private $eventStore;

    public function __construct(EventStore $eventStore)
    {
        $this->eventStore = $eventStore;

        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->eventStore->create(new Stream(new StreamName('foo_stream'), new \ArrayIterator()));
        $this->eventStore->create(new Stream(new StreamName('bar_stream'), new \ArrayIterator()));
    }
}

And then call it via acme:create-event-streams instead of the original call event-store:event-stream:create.