This example demonstrates EventSourcing with symfony & flow.
With Docker
cd .docker
Change the path to your local directory
vim .env
docker-compose up -d
Execute into the event-sourcing.app container
docker exec -it eventsourcing-app bash
composer install
Create the event store(s)
php bin/console eventsourcing:store-setup
Migrate the domain models (for this example)
php bin/console doctrine:migrations:migrate
Open your browser and insert localhost
With Vagrant / local Web Server
Navigate to your project directory. Create the event store(s) with the following console command:
php bin/console eventsourcing:store-setup
Migrate the domain models (for this example)
php bin/console doctrine:migrations:migrate
In the symfony controller the commands are created and routed to the corresponding command handler. For the blogging context the BlogController creates the CreateBlog command and routes it to the BloggingCommandHandler. In the handleCreateBlog function the event is stored.
Example event: BlogWasCreated.php
class BlogWasCreated implements DomainEventInterface
{
/**
* @var string
*/
private $name;
/**
* @var UserIdentifier
*/
private $author;
/**
* @var string
*/
private $streamName;
/**
* BlogWasCreated constructor.
* @param string $name
* @param UserIdentifier $author
* @param string $streamName
*/
public function __construct(
string $name,
UserIdentifier $author,
string $streamName
)
{
$this->name = $name;
$this->author = $author;
$this->streamName = $streamName;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return UserIdentifier
*/
public function getAuthor(): UserIdentifier
{
return $this->author;
}
/**
* @return string
*/
public function getStreamName(): string
{
return $this->streamName;
}
}
<?php
$event = new BlogWasCreated(
$command->getName(),
$command->getAuthorIdentifier(),
$streamName
);
$stream = StreamName::fromString($streamName);
$this->eventStore->commit($stream, DomainEvents::withSingleEvent(
$event
));
The BloggingCommandHandler can also react to a read command. The function handleStream returns the event stream.
<?php
$streamName = StreamName::fromString('some-stream');
$eventStream = $this->eventStore->load(StreamName::fromString($streamName))
In order to react upon new events you'll need an event listener. In the Flow EventSourcing package the listeners are called with the when*" namings. For the event BlogWasCreated the listenerMethodName is 'whenBlogWasCreated'.
$listenerMethodName = 'when' . (new \ReflectionClass($event))->getShortName();
Example projector: BlogListProjector.php
<?php
class BlogListProjector implements ProjectorInterface, EventSubscriberInterface
{
private $blogRepository;
public function __construct(BlogRepository $blogRepository)
{
$this->blogRepository = $blogRepository;
}
public static function getSubscribedEvents()
{
return [
// NOTE!!! you always have to use "when*" namings, as otherwise, the EventListenerInvoker
// will not properly call the right methods here.
// we only use the EventSubscriber from symfony to figure out which listeners should be called.
BlogWasCreated::class => ['whenBlogWasCreated']
];
}
public function whenBlogWasCreated(BlogWasCreated $event, RawEvent $rawEvent)
{
}
}
In our example listener (BlogListProjector) we need the function whenBlogWasCreated(...). Now this listener can be used by the event invoker of the EventSourcing package.
$this->eventListener->$listenerMethodName($event, $rawEvent);
Note: The listener has to implement the EventSubscriberInterface
and ProjectorInterface
. Otherwise it will not work properly.
With the following command you can rebuild all the projections.
bin/console eventsourcing:projection-replay-demo
Perhaps you have to change the file permissions