Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
prolic committed Mar 25, 2017
1 parent 379389e commit 2a21836
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 118 deletions.
7 changes: 5 additions & 2 deletions docs/interop_factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ configuration to our application configuration:
If you want to speed up loading of aggregates with a snapshot store then you need to make
it available as service in the container and use the configuration to let the factory inject the snapshot store in the repository.

Also you need to install [Prooph SnapshotStore](https://github.com/prooph/snapshot-store) and a persistable implementation of it,
like [pdo-snapshot-store](https://github.com/prooph/pdo-snapshot-store/) or [mongodb-snapshot-store](https://github.com/prooph/mongodb-snapshot-store/).

```php
[
'prooph' => [
Expand All @@ -61,13 +64,13 @@ it available as service in the container and use the configuration to let the fa
],
],
],
// zf2 service manager example to configure snapshot store service below
// zf3 service manager example to configure snapshot store service below
'dependencies' => [
'aliases' => [
'awesome_snapshot_store' => InMemorySnaphotStore::class,
],
'factories' => [
InMemorySnaphotStore::class => InvokaleFactory::class,
InMemorySnaphotStore::class => InvokableFactory::class,
],
],
]
Expand Down
4 changes: 2 additions & 2 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ then you can implement the `Prooph\EventSourcing\Aggregate\AggregateTranslator`

## Snapshot Store

The snapshot store is now a simple interface, see `Prooph\EventSourcing\Snapshot\SnapshotStore`. The adapters are all removed
and replaced by different snapshot store implementation, f.e. `Prooph\EventSourcing\Snapshot\InMemorySnapshotStore`.
The snapshot store is now a simple interface, see `Prooph\SnapshotStore\SnapshotStore`. The adapters are all removed
and replaced by different snapshot store implementation, f.e. `Prooph\SnapshotStore\InMemorySnapshotStore`.

## Aggregate Root

Expand Down
75 changes: 11 additions & 64 deletions docs/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,81 +26,29 @@ And the repository must also be able to load persisted events from a stream and
To provide this functionality the repository makes use of various helper classes explained below.

## AggregateType
Each repository is responsible for one `Prooph\EventStore\Aggregate\AggregateType`. Super types are not supported.
Imagine we have a domain with `Admin extends User` and `Employee extends User`. You'd need to have a `AdminRepository` and
a `EmployeeRepository` in this case. If this is not what you want you can create a custom aggregate translator (see below)
which is capable of reconstituting the correct types based on information derived from persisted domain events.
Then you can have a `UserRepository` set up with your custom aggregate translator and it should work.
Each repository is responsible for one `\Prooph\EventSourcing\Aggregate\AggregateType`.

## AggregateTranslator

To achieve 100% decoupling between layers and/or contexts you can make use of translation adapters.
For prooph/event-store such a translation adapter is called an `Prooph\EventStore\Aggregate\AggregateTranslator`.
For prooph/event-store such a translation adapter is called a `Prooph\EventSourcing\Aggregate\AggregateTranslator`.

The interface requires you to implement 5 methods:

- extractAggregateId
- extractAggregateVersion
- extractPendingStreamEvents
- reconstituteAggregateFromHistory
- applyStreamEvents
- replayStreamEvents

To make your life easier prooph/event-store ships with a `Prooph\EventStore\Aggregate\ConfigurableAggregateTranslator` which implements the interface.

Let's have a look at the constructor

```php
public function __construct(
string $identifierMethodName = null,
string $versionMethodName = null,
string $popRecordedEventsMethodName = null,
string $replayEventsMethodsName = null,
string $staticReconstituteFromHistoryMethodName = null,
string $eventToMessageCallback = null,
string $messageToEventCallback = null)
{
//...
}
```

We can identify 7 dependencies but all are optional.

- `$identifierMethodName`
- defaults to `getId`
- used to `extractAggregateId` and must return a string
- you can have a translator per aggregate type, so if you prefer to have methods reflecting domain language you likely want to use methods like `getTrackingId`, `getProductNumber`, etc.. As you can see, this is no problem for the event store. Feel free to model your aggregates exactly the way you need it!
- `$versionMethodName`
- defaults to `getVersion`
- used to `extractVersion` of the aggregate root
- `$popRecordedEventsMethodName`
- defaults to `popRecordedEvents`
- with this method the `ConfigurableAggregateTranslator` requests the latest recorded events from your aggregate
- the aggregate should also clear its internal event cache before returning the events as no additional method is invoked
- `replayStreamEvents`
- defaults to `replay`
- used in case the repository loaded a snapshot and needs to replay newer events
- `$staticReconstituteFromHistoryMethodName`
- defaults to `reconstituteFromHistory`
- like indicated in the parameter name the referenced method must be static (a named constructor) which must return an instance of the aggregate with all events replayed
- `$eventToMessageCallback`
- completely optional
- you can pass any callable
- the callable is invoked for each domain event returned by `$popRecordedEventsMethodName` and can be used to translate a domain event into a `Prooph\Common\Messaging\Message`
- the message interface is required by the event store implementations to function correctly
- you can also decide to let your domain events implement the interface. This would make your life easier when you want to make use of advanced features provided by prooph. But, again. Your domain events don't have to implement the interface. It is your choice!
- `$messageToEventCallback`
- completely optional
- it is the opposite of `$eventToMessageCallback`
- when you pass a callable it is invoked for each message (loaded from the event store) before `$staticReconstituteFromHistoryMethodName` or `$applyEventsMethodsName`is called


*Note: When using the translation callbacks shown above you should consider translating domain events into `Prooph\Common\Messaging\DomainEvent` objects. It is a default implementation of the `Message` interface and all event store implementations can handle it out-of-the-box.
If you decide to provide your own implementation of `Prooph\Common\Messaging\Message` you should have a look at `Prooph\Common\Messaging\MessageFactory` and `Prooph\Common\Messaging\MessageConverter` because the event store implementations work with these to translate events into PHP arrays and back.*
To make your life easier prooph/event-sourcing ships with a `\Prooph\EventSourcing\EventStoreIntegration\AggregateTranslator` which implements the interface.

## Snapshot Store

A repository can be set up with a snapshot store to speed up loading of aggregates.
Checkout the snapshot docs for more information.

You need to install [Prooph SnapshotStore](https://github.com/prooph/snapshot-store) and a persistable implementation of it,
like [pdo-snapshot-store](https://github.com/prooph/pdo-snapshot-store/) or [mongodb-snapshot-store](https://github.com/prooph/mongodb-snapshot-store/).

## Event Streams

Expand All @@ -112,7 +60,6 @@ in a `user_stream`.

The repository can also be configured to create a new stream for each new aggregate instance. You need to turn the last
constructor parameter `oneStreamPerAggregate` to true to enable the mode.
This can be useful when working for example with MongoDB and you want to persist all events of an aggregate in single document (take care of the document size limit).
If the mode is enabled the repository builds a unique stream name for each aggregate by using the `AggregateType` and append
the `aggregateId` of the aggregate. The stream name for a new `Acme\User` with id `123` would look like this: `Acme\User-123`.

Expand All @@ -121,18 +68,18 @@ Check your event store implemtation of choice for details. You can also override
for building the stream name.

## Wiring It Together
Best way to see a repository in action is by looking at the `ProophTest\EventStore\Aggregate\AggregateRepositoryTest`.
Best way to see a repository in action is by looking at the `\ProophTest\EventSourcing\Aggregate\AggregateRepositoryTest`.

### Set Up

```php
$this->repository = new AggregateRepository(
$this->eventStore,
AggregateType::fromAggregateRootClass('ProophTest\EventStore\Mock\User'),
new ConfigurableAggregateTranslator()
AggregateType::fromAggregateRootClass('ProophTest\EventSourcing\Mock\User'),
new AggregateTranslator()
);

$this->eventStore->create(new Stream(new StreamName('event_stream'), []));
$this->eventStore->create(new Stream(new StreamName('event_stream'), new ArrayIterator()));
```

Notice the injected dependencies! Snapshot store, stream name and stream mode are optional and not injected for all tests.
Expand Down
52 changes: 2 additions & 50 deletions docs/snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,60 +11,12 @@ in most cases.
If aggregate reconstitution gets slow you can add an additional layer to the system which
is capable of providing aggregate snapshots.

Choose one of the `Prooph\*SnapshotStore` to take snapshots.
Choose one of the `Prooph\SnapshotStore\*` to take snapshots.
Inject the snapshot store into an aggregate repository and the repository will use the snapshot store to speed up
aggregate loading.

Our example application [proophessor-do](https://github.com/prooph/proophessor-do) contains a snapshotting tutorial.

*Note: All SnapshotStores ship with interop factories to ease set up.*

## Creating snapshot projections

Basically there are two possibilities:

First, if you are using a single stream or one stream per aggregate type, in this case you need to
create a projection from that stream.

Second, if you are using one stream per aggregate, you need to create the stream from the category,
when you have no category stream created (see: [StandardProjection](https://github.com/prooph/standard-projections/)).

All you have to do to create a snapshot projection is to create a small and simple script like this:

```php
$projection = $eventStore->createReadModelProjection(
'user_snapshots',
new \Prooph\EventSourcing\Aggregate\SnapshotReadModel(
$aggregateRepository,
$aggregateTranslator,
$snapshotStore
)
);

$projection
->fromStream('user_stream')
->whenAny(function ($state, Message $event): void {
$this->readModel()->stack('replay', $event);
})
->run();
```

or in case you need to create the projection from category:

```php
$projection = $eventStore->createReadModelProjection(
'user_snapshots',
new \Prooph\EventSourcing\Aggregate\SnapshotReadModel(
$aggregateRepository,
$aggregateTranslator,
$snapshotStore
)
);

$projection
->fromCategory('user')
->whenAny(function ($state, Message $event): void {
$this->readModel()->stack('replay', $event);
})
->run();
```
For more informations about snapshots and how to configure them, see [Prooph Snapshotter](https://github.com/prooph/snapshotter).
1 change: 1 addition & 0 deletions src/Container/Aggregate/AggregateRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Prooph\EventSourcing\Container\Aggregate;
Expand Down

0 comments on commit 2a21836

Please sign in to comment.