-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #131 from codeliner/feature/ar_factory
Add AbstractAggregateRootFactory
- Loading branch information
Showing
9 changed files
with
468 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Interop Factories | ||
|
||
Instead of providing a module, a bundle, a bridge or similar framework integration prooph/event-store ships with `interop factories`. | ||
|
||
## Factory-Driven Creation | ||
|
||
The concept behind these [factories](../src/Container) is simple but powerful. It allows us to provide you with bootstrapping logic for the event store and related components | ||
without the need to rely on a specific framework. However, the factories have three requirements. | ||
|
||
### Requirements | ||
|
||
1. Your Inversion of Control container must implement the [interop-container interface](https://github.com/container-interop/container-interop). | ||
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 | ||
always bootstrap the components by hand. Just look at the factories for inspiration in this case.* | ||
|
||
### Event Store Factory | ||
|
||
If the requirements are met you just need to add a new section in your application config ... | ||
|
||
```php | ||
[ | ||
'prooph' => [ | ||
'event_store' => [ | ||
'adapter' => [ | ||
'type' => 'adapter_service_id', //The factory will use this id to get the adapter from the container | ||
//The options key is reserved for adapter factories | ||
'options' => [] | ||
], | ||
'event_emitter' => 'emitter_service_id' //The factory will use this id to get the event emitter from the container | ||
'plugins' => [ | ||
//And again the factory will use each service id to get the plugin from the container | ||
//Plugin::setUp($eventStore) is then invoked by the factory so your plugins get attached automatically | ||
//Awesome, isn't it? | ||
'plugin_1_service_id', | ||
'plugin_2_service_id', | ||
//... | ||
] | ||
] | ||
], | ||
//... other application config here | ||
] | ||
``` | ||
|
||
... and register the [EventStoreFactory](../src/Container/EventStoreFactory.php) in your IoC container. We recommend using the service id `Prooph\EventStore\EventStore (EventStore::class)` for the event store | ||
because other factories like the [stream factories](../src/Container/Stream) try to locate the event store | ||
by using this service id. | ||
|
||
*Note: The available event store adapters also ship with factories. Please refer to the adapter packages for details.* | ||
|
||
### AbstractAggregateRepositoryFactory | ||
|
||
To ease set up of repositories for your aggregate roots prooph/event-store also ships with a [AbstractAggregateRepositoryFactory](../src/Aggregate/AbstractAggregateRepositoryFactory.php). | ||
It is an abstract class implementing the `container-interop RequiresContainerId` interface. The `containerId` method | ||
itself is not implemented in the abstract class. You have to extend it and provide the container id because each | ||
aggregate repository needs a slightly different configuration and therefor needs its own config key. | ||
|
||
*Note: You can have a look at the [RepositoryMock](../tests/Mock/RepositoryMock.php). It sounds more complex than it is.* | ||
|
||
Let's say we have a repository factory for a User aggregate root. We use `user_repository` as container id and add this | ||
configuration to our application configuration: | ||
|
||
```php | ||
[ | ||
'prooph' => [ | ||
'event_store' => [ | ||
'user_repository' => [ //<-- here the container id is referenced | ||
'repository_class' => MyUserRepository::class, //<-- FQCN of the repository responsible for the aggregate root | ||
'aggregate_type' => MyUser::class, //<-- The aggregate root FQCN the repository is responsible for | ||
'aggregate_translator' => 'user_translator', //<-- The aggregate translator must be available as service in the container | ||
] | ||
] | ||
] | ||
] | ||
``` | ||
|
||
If you also want to configure a custom stream strategy or want to make use of a snapshot adapter then you need to make | ||
them available as services in the container and use the configuration to let the factory inject them in the repository. | ||
|
||
```php | ||
[ | ||
'prooph' => [ | ||
'event_store' => [ | ||
'user_repository' => [ | ||
'repository_class' => MyUserRepository::class, | ||
'aggregate_type' => MyUser::class, | ||
'aggregate_translator' => 'user_translator', | ||
'stream_strategy' => 'user_stream' // <-- Custom stream strategy service id | ||
'snapshot_store' => 'awesome_snapshot_store' // <-- SnapshotStore service id | ||
] | ||
] | ||
] | ||
] | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
src/Container/Aggregate/AbstractAggregateRepositoryFactory.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?php | ||
/* | ||
* This file is part of the prooph/event-store. | ||
* (c) 2014-2015 prooph software GmbH <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
* | ||
* Date: 10/21/15 - 6:10 PM | ||
*/ | ||
namespace Prooph\EventStore\Container\Aggregate; | ||
|
||
use Interop\Config\ConfigurationTrait; | ||
use Interop\Config\RequiresContainerId; | ||
use Interop\Config\RequiresMandatoryOptions; | ||
use Interop\Container\ContainerInterface; | ||
use Prooph\EventStore\Aggregate\AggregateRepository; | ||
use Prooph\EventStore\Aggregate\AggregateType; | ||
use Prooph\EventStore\EventStore; | ||
use Prooph\EventStore\Exception\ConfigurationException; | ||
|
||
/** | ||
* Class AbstractAggregateRepositoryFactory | ||
* | ||
* @package Prooph\EventStore\Container | ||
*/ | ||
abstract class AbstractAggregateRepositoryFactory implements RequiresContainerId, RequiresMandatoryOptions | ||
{ | ||
use ConfigurationTrait; | ||
|
||
/** | ||
* @param ContainerInterface $container | ||
* @throws ConfigurationException | ||
* @return AggregateRepository | ||
*/ | ||
public function __invoke(ContainerInterface $container) | ||
{ | ||
$config = $container->get('config'); | ||
$config = $this->options($config); | ||
|
||
$repositoryClass = $config['repository_class']; | ||
|
||
if (! class_exists($repositoryClass)) { | ||
throw ConfigurationException::configurationError(sprintf('Repository class %s cannot be found', $repositoryClass)); | ||
} | ||
|
||
if (! is_subclass_of($repositoryClass, AggregateRepository::class)) { | ||
throw ConfigurationException::configurationError(sprintf('Repository class %s must be a sub class of %s', $repositoryClass, AggregateRepository::class)); | ||
} | ||
|
||
$eventStore = $container->get(EventStore::class); | ||
$aggregateType = AggregateType::fromAggregateRootClass($config['aggregate_type']); | ||
$aggregateTranslator = $container->get($config['aggregate_translator']); | ||
|
||
$streamStrategy = isset($config['stream_strategy'])? $container->get($config['stream_strategy']) : null; | ||
|
||
$snapshotStore = isset($config['snapshot_store'])? $container->get($config['snapshot_store']) : null; | ||
|
||
return new $repositoryClass($eventStore, $aggregateType, $aggregateTranslator, $streamStrategy, $snapshotStore); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function vendorName() | ||
{ | ||
return 'prooph'; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function packageName() | ||
{ | ||
return 'event_store'; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function mandatoryOptions() | ||
{ | ||
return [ | ||
'repository_class', | ||
'aggregate_type', | ||
'aggregate_translator', | ||
]; | ||
} | ||
} |
Oops, something went wrong.