Skip to content

Commit

Permalink
finish docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Dec 23, 2021
1 parent 7d0ee16 commit 91e68f9
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 14 deletions.
50 changes: 50 additions & 0 deletions docs/processor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Processor

The `processor` is a kind of [event bus](./event_bus.md) listener that can execute actions on certain events.
A process can be for example used to send an email when a guest is checked in:

```php
namespace App\Domain\Hotel\Listener;

use App\Domain\Hotel\Event\GuestIsCheckedIn;
use Patchlevel\EventSourcing\Aggregate\AggregateChanged;
use Patchlevel\EventSourcing\EventBus\Listener;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

final class SendCheckInEmailListener implements Listener
{
private MailerInterface $mailer;

private function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}

public function __invoke(AggregateChanged $event): void
{
if (!$event instanceof GuestIsCheckedIn) {
return;
}

$email = (new Email())
->from('[email protected]')
->to('[email protected]')
->subject('Guest is checked in')
->text(sprintf('A new guest named "%s" is checked in', $event->guestName()));

$this->mailer->send($email);
}
}
```

If you have the symfony default service setting with `autowire`and `autoconfiger` enabled,
the processor is automatically recognized and registered at the `Listener` interface.
Otherwise you have to define the processor in the symfony service file:

```yaml
services:
App\Domain\Hotel\Listener\SendCheckInEmailListener:
tags:
- event_sourcing.processor
```
82 changes: 82 additions & 0 deletions docs/projection.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,90 @@ and everything can always be reproduced from the events.
The target of a projection can be anything.
Either a file, a relational database, a no-sql database like mongodb or an elasticsearch.

## Define Projection

In this example we are simply mapping hotel statistics:

```php
namespace App\Projection;

use App\Domain\Hotel\Event\HotelCreated;
use App\Domain\Hotel\Event\GuestIsCheckedIn;
use App\Domain\Hotel\Event\GuestIsCheckedOut;
use Doctrine\DBAL\Connection;
use Patchlevel\EventSourcing\Projection\Projection;

final class HotelProjection implements Projection
{
private Connection $db;

public function __construct(Connection $db)
{
$this->db = $db;
}

public static function getHandledMessages(): iterable
{
yield HotelCreated::class => 'applyHotelCreated';
yield GuestIsCheckedIn::class => 'applyGuestIsCheckedIn';
yield GuestIsCheckedOut::class => 'applyGuestIsCheckedOut';
}

public function applyHotelCreated(HotelCreated $event): void
{
$this->db->insert(
'hotel',
[
'id' => $event->aggregateId(),
'name' => $event->hotelName(),
'guests' => 0
]
);
}

public function applyGuestIsCheckedIn(GuestIsCheckedIn $event): void
{
$this->db->executeStatement(
'UPDATE hotel SET guests = guests + 1 WHERE id = ?;',
[$event->aggregateId()]
);
}

public function applyGuestIsCheckedOut(GuestIsCheckedOut $event): void
{
$this->db->executeStatement(
'UPDATE hotel SET guests = guests - 1 WHERE id = ?;',
[$event->aggregateId()]
);
}

public function create(): void
{
$this->db->executeStatement('CREATE TABLE IF NOT EXISTS hotel (id VARCHAR PRIMARY KEY, name VARCHAR, guests INTEGER);');
}

public function drop(): void
{
$this->db->executeStatement('DROP TABLE IF EXISTS hotel;');
}
}
```

If you have the symfony default service setting with `autowire`and `autoconfiger` enabled,
the projection is automatically recognized and registered at the `Projection` interface.
Otherwise you have to define the projection in the symfony service file:

```yaml
services:
App\Projection\HotelProjection:
tags:
- event_sourcing.projection
```
## Projection commands
The bundle also provides a few commands to create, delete or rebuild projections:
```bash
bin/console event-sourcing:projection:create
bin/console event-sourcing:projection:drop
Expand Down
30 changes: 22 additions & 8 deletions docs/repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,28 @@ The [design pattern](https://martinfowler.com/eaaCatalog/repository.html) of the
Every aggregate needs a repository to be stored.
And each repository is only responsible for one aggregate.

## Explicit dependency injection
## Use repositories

The service `@event_sourcing.repository.profile` with suffix `profile` is created magically from the configuration above.
In your own repository, use this configuration to auto-wire the PatchLevel repository accordingly to your aggregate.
Every aggregate that has been defined and registered automatically has a repository.
These repositories can also be auto-updated.
To do this, you have to use the Typehint repository and structure the variable as follows.
Aggregate name with a `Repository` suffix. For example we have the aggregate `hotel`,
then you can build the typhint as follows: `Patchlevel\EventSourcing\Repository\Repository $hotelRepository`.

```yaml
services:
App\Domain\Hotel\HotelRepository:
arguments:
$repository: '@event_sourcing.repository.profile'
```php
use Patchlevel\EventSourcing\Repository\Repository;

final class HotelController
{
private Repository $hotelRepository;

public function __construct(Repository $hotelRepository)
{
$this->hotelRepository = $hotelRepository;
}

// ...
}
```

> :book: You can find out more about autowire [here](https://symfony.com/doc/current/service_container.html#binding-arguments-by-name-or-type)
15 changes: 13 additions & 2 deletions docs/tools.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
# Tools

The bundle offers even more DX tools, which are listed here.

## Show events

You can display all events for a specific aggregate:

```bash
bin/console event-sourcing:show aggregate id
```

## Watch events

dev config:
There is also a watch server, because you can start to see all events in real time.
To do this, you have to add the following configuration for the dev environment:

```yaml
patchlevel_event_sourcing:
watch_server:
enabled: true
```
> :warning: Use this configuration for dev purposes only!
There is then a new command to start the watch server:
```bash
bin/console event-sourcing:watch
```
```

> :book: The command can be terminated with `ctrl+c` or `control+c`.
8 changes: 4 additions & 4 deletions src/DependencyInjection/PatchlevelEventSourcingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private function configureEventBus(array $config, ContainerBuilder $container):
$container->setAlias(EventBus::class, DefaultEventBus::class);

$container->registerForAutoconfiguration(Listener::class)
->addTag('event_sourcing.event_listener');
->addTag('event_sourcing.processor');

return;
}
Expand All @@ -126,7 +126,7 @@ private function configureEventBus(array $config, ContainerBuilder $container):
$container->setAlias(EventBus::class, $config['event_bus']['service']);

$container->registerForAutoconfiguration(Listener::class)
->addTag('event_sourcing.event_listener');
->addTag('event_sourcing.processor');
}

/**
Expand All @@ -140,7 +140,7 @@ private function configureProjection(array $config, ContainerBuilder $container)
if (isset($config['event_bus']) && $config['event_bus']['type'] === 'symfony') {
$projectionListener->addTag('messenger.message_handler', ['bus' => $config['event_bus']['service']]);
} else {
$projectionListener->addTag('event_sourcing.event_listener');
$projectionListener->addTag('event_sourcing.processor');
}

$container->registerForAutoconfiguration(Projection::class)
Expand Down Expand Up @@ -361,7 +361,7 @@ private function configureWatchServer(array $config, ContainerBuilder $container
if (isset($config['event_bus']) && $config['event_bus']['type'] === 'symfony') {
$listener->addTag('messenger.message_handler', ['bus' => $config['event_bus']['service']]);
} else {
$listener->addTag('event_sourcing.event_listener');
$listener->addTag('event_sourcing.processor');
}

$container->register(DefaultWatchServer::class)
Expand Down

0 comments on commit 91e68f9

Please sign in to comment.