Skip to content

Commit

Permalink
Merge pull request #60 from prooph/docs
Browse files Browse the repository at this point in the history
Docs
  • Loading branch information
codeliner committed Mar 26, 2017
2 parents 90395f0 + 7c9f037 commit 66079a8
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 190 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ before_script:
script:
- if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit; fi
- if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi
- if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check src/ tests/; fi
- if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check examples/ src/ tests/; fi

after_success:
- if [[ $TEST_COVERAGE == 'true' ]]; then php vendor/bin/coveralls -v; fi
Expand Down
8 changes: 4 additions & 4 deletions docs/inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ final class UserAggregateTranslator extends \Prooph\EventSourcing\EventStoreInte
final class EventStoreUserCollection extends
\Prooph\EventStore\Aggregate\AggregateRepository
{
public function add(User $user)
public function save(User $user): void
{
$this->addAggregateRoot($user);
$this->saveAggregateRoot($user);
}
public function get(UserId $userId)
public function get(UserId $userId): ?User
{
return $this->getAggregateRoot($userId->toString());
}
Expand All @@ -111,7 +111,7 @@ final class EventStoreUserCollection extends
```php
final class EventStoreUserCollectionFactory
{
public function __invoke(ContainerInterface $container)
public function __invoke(ContainerInterface $container): EventStoreUserCollection
{
return new EventStoreUserCollection(
$container->get(EventStore::class),
Expand Down
11 changes: 7 additions & 4 deletions docs/interop_factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ without the need to rely on a specific framework. However, the factories have th

### Requirements

1. Your Inversion of Control container must implement the [interop-container interface](https://github.com/container-interop/container-interop).
1. Your Inversion of Control container must implement the [PSR Container interface](https://github.com/php-fig/container).
2. [interop-config](https://github.com/sandrokeil/interop-config) must be installed
3. The application configuration should be registered with the service id `config` in the container.

*Note: Don't worry, if your environment doesn't provide the requirements. You can
*Note: Don't worry, if your environment doesn't provide these requirements, you can
always bootstrap the components by hand. Just look at the factories for inspiration in this case.*

### AggregateRepositoryFactory
Expand Down 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 have all been removed
and replaced with different snapshot store implementation, f.e. `Prooph\SnapshotStore\InMemorySnapshotStore`.

## Aggregate Root

Expand Down
79 changes: 14 additions & 65 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 @@ -110,9 +58,8 @@ If you wish to use another name, you can pass a custom `Prooph\EventStore\Stream
This is especially useful when you want to have an event stream per aggregate type, for example store all user related events
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
The repository can also be configured to create a new stream for each new aggregate instance. You'll 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,19 @@ 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 Expand Up @@ -206,6 +154,7 @@ The test case has some more tests including snapshot usage and working with diff
Just browse through the test methods for details.

## Loading of thousands aggregates

If you need to load thousands of aggregates for reading only, your memory can be exhausted, because the
`AggregateRepository` uses an identity map. So every loaded aggregate is stored there, unless a commit is executed. If
you have a read only process, you should consider to clear the identity map at some time. This can be done by calling
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).
Loading

0 comments on commit 66079a8

Please sign in to comment.