prooph / event-store Goto Github PK
View Code? Open in Web Editor NEWPHP 7.4 EventStore Implementation
Home Page: http://getprooph.org
License: BSD 3-Clause "New" or "Revised" License
PHP 7.4 EventStore Implementation
Home Page: http://getprooph.org
License: BSD 3-Clause "New" or "Revised" License
Currently we pass an array of options to any adapter constructor.
What would you think of passing a configuration object? You can still have an array of options, but you would pass them to the config object, instead of the adapter directly.
Something like this:
$config = new Config($someArray);
$adapter = new SomeAdapter();
$config->configureAdapter($adapter);
This is the same approach zf2 takes for the service manager, see: https://github.com/zendframework/zend-servicemanager/blob/master/src/Config.php
Downsides: We have to add public getters and setters to any adapter. For security reasons, we could disallow setting them twice (throwing an exception).
If accepted I could take this job over the weekend.
Thoughts?
Provide an example and may improve the ES API to ease usage of custom domain events.
This is the epic for a major change in the EventStore library. From version 0.5.0 on the ES will only handle event streams. The repository and aggregate type concept will be removed. But the library will continue to support aggregates and their repositories. Different StreamStrategies will be available to support translation from aggregates to streams and back and to organize events of the aggregates in one or more streams.
Implement ES-Feature to support Buttercup.protects Aggregates and DomainEvents.
Sub-Task of #20
If you want to handle follow up commands in the same transaction the event store needs to support nested transactions.
Adapter::commit should only be called when the last open transaction is going to be commited and the commit.post event should only be triggered when all transactions are closed. On the other hand the commit.pre event should be triggered on every EventStore::commit call. The EventStore should set a is_nested_transaction flag in the commit.pre event to give listeners the chance to do their work only on real transaction commit.
For the AggregateRepositories it is important, that the commit.pre event is triggered on every commit call because a repository can register to the event in a nested transaction and is otherwise not called.
Instead of using php namespace separator both VOs should normalize a namespace so that it is separated by dots instead of backslashes.
Please consider to change persistent adpaters and EventSourcing\AggregateChangedEventHydrator, too!
The ES should only be responsible for CRUD of event streams with the help of a persistence adapter.
Sub-Task of #19
We should provide possibilities to format event data before it gets persisted. Not everybody is happy with the current way of persisting information f.e. the format used to store datetimes or the column type for UUIDs (allow binary type for DoctrineDBAL >=2.5).
See datetime discussion for zend-log
Adapters should implement the FeatureInterface CreateStreamIfNotExists when they provide such a functionality. It should be possible to turn the mechanism on or off.
EventStore has to check if adapter can handle on the fly generation and throw an exception if not.
EventStore should trigger the creation only when a new entity should be persisted and the store has no entity of same type in the identity map.
EventStore provide event-driven possiblity to abort the try.
See geteventstore.org for inspiration.
Sub-Task of #19
Based on the new factories we should now also provide a more flexible AggregateTranslator.
In DDD you don't want to couple your model with the infrastructure so prooph/event-sourcing is out.
What we can do instead is to provide a configurable AggregateTranslator.
The translator should provide:
name
and visibility
of method to get the aggregate idname
and visibility
of method to get pending stream eventsname
, visibility
and static = true/false
of method to reconstitute an aggregateWe can work with inheritance here to provide defaults but allow overriding them.
Note: The AggregateTranslator no longer needs to provide DomainEvent
s but instead Message
s
See #61
Depends on #60
public function __construct(EventStore $eventStore, AggregateType $superclass, array $aggregateTypeStreamMap = array())
{
$this->eventStore = $eventStore;
$this->streamName = $this->buildStreamName($superclass);
$this->aggregateTypeStreamMap = $aggregateTypeStreamMap;
}
The MappedSuperclassStreamStrategy constructor executes streamName attribute initialization before aggregateTypeStreamMap attribute initialization, but buildStreamName uses aggregateTypeStreamMap attribute.
Given that order of inizialization $aggregateTypeStreamMap parameter is never considered and the streamName isn't inizialized properly.
Since zend-servicemanager v2.6 the zend service manager implements ContainerInterface
.
So a zend specific factory is no longer needed and we can provide invokable factories instead. This allows the usage in any environment working with a ContainerInterface
. The only requirement is is that a application wide config
is registered as a service
in the container. But this is a common and useful feature anyway.
Example Factory
final class EventStoreFactory
{
public function __invoke(ContainerInterface $container)
{
$eventStoreConfig = $container->get(\Prooph\EventStore\Configuration\Configuration::class);
if ($container->has(\Prooph\Common\Messaging\MessageFactory::class) {
$eventStoreConfig->setMessageFactory(
$container->get(\Prooph\Common\Messaging\MessageFactory::class);
);
}
//get ES Adapter and other aspects ...
return new EventStore($eventStoreConfig);
}
}
Such a factory will centralize the event store initialization and increase re usability of the event-store component a lot.
The event store Configuration should pass a Prooph\Common\Messaging\MessageFactory
and a Prooph\Common\Messaging\MessageConverter
as options to the event store adapter.
If no custom implementations were given the default implementations provided by prooph/common should be used.
We need snapshots to replay Aggregates but also to regenerate read models
Start using rtd to document the lib
Avoid loading all events from database at once. Allow the use of an iterator so event store adapters can provide a \Traversable instead of a domain event collection.
It should be possible to add meta data to events like the related aggregate type or aggregate id or some other attributes that are candidates for an index.
Sub-Task of #19
Since prooph/common 2.1 a default ActionEvent implementation is available and should be used instead of the Zf2ActionEvent.
Based on this conversation prooph/event-store-mongodb-adapter#5
In preparation for a message tracking component the event store should provide the possibility to customize the stream schema. This allows to add status flags like stream event was published etc.
A simple schema configuration with property name => property data type mapping should be enough for a first version.
This issue depends on:
and is related to prooph/service-bus#24
Currently the FeatureManager
extends ZF2\AbstractPluginManager. In PES 5.0 we want to decouple the event-store from specific external dependencies. Therefor the FeatureManager
will use the Interop\ContainerInterface to load features.
I'm wanting to learn using events stores, can you provide wiki/documentation for using this package?
Also if there is any resources that you can link that would be great I've read lots from what Greg Young has said.
Check if EventStore is in a transaction before trigger a rollback, otherwise db adapter throws an exception.
Here is my first feedback (as requested in #14 ๐).
I find the naming of Prooph\EventStore\Repository\RepositoryInterface
a bit confusing since it describes not a repository (at least as i understand it) but rather an adapter (or something) which the event store uses to extract event streams from an AR and to create AR's from event streams. It acts like a bridge between the event store and and the domain model.
I stumbled over this while integrating ProophEventStore with my application and i looked at how ProophEventSourcing integrates with ProophEventStore.
The EventSourcingRepository
implements the RepositoryInterface
and acts both as a repository (as i understand it) and a bridge for ProophEventStore.
A side effect is, that if you use a DIC, it would create a circular dependency as the EventSourcingRepository
requires an EventStore
instance and the EventStore
requires a RepositoryInterface
instance if you pass it through the $repositoryMap
configuration.
Just a side note: The intergration of ProophEventSourcing into ProophEventStore is cluttered over 3 namspaces (Prooph\EventSourcing\EventStoreFeature
, Prooph\EventSourcing\Mapping
, Prooph\EventSourcing\Repository
) which was a little bit confusing.
Other than that, nice work! ๐
Once i figured that out, integrating ProophEventStore into my application was pretty straightforward.
... to support prototype use cases without read model
Repository needs the method getAll() and Adapter needs the method loadStreams()
Maybe #37 + some documentation about how to use the event store to replay read models is enough.
We need to think about the following questions:
load
and loadEventsByMetadataFrom
enough to handle common replay scenarios?Currently, the Pre- and PostCommitEvents are based on ZF2\Event. In PES 5.0 they should only extend Prooph\Common\ActionEvent.
It should be possible to define a super class as AggregateType for a repository but use the concret class of the passed aggregate to store it. A special form of the SingleStreamStrategy (maybe a MappedSuperClassStreamStrategy) and AggregateTranslator are required to store and load an aggregate without considering the AggregateType of the super class but the concret class.
It is not enough to only use the FQCN of an Event as identifier to reconstruct the event from a stream. A DomainEventHydratorManager could provide more strategies to reconstruct events.
Posibilities could be:
FQCN should be translated to dot notation or events implement EventNameProviderInterface
Based on this conversation: prooph/event-store-mongodb-adapter#5
The whole EventStore#getRepository implementation should be event-driven.
Checking for a special repo of an aggregate should be a listener with priority 100. Default behavior should be added as listener with low priority like -100. A third party component can then register a listener with prio 0 and provide a custom repo for a group of entities but not all entities.
Sub-Task of #19
The AggregateManager should be reponsible for managing current state of an aggregate. It knows if aggregate is already persisted, holds the current version of the aggregate to detect concurrent logging issues and is responsible for making and providing snapshots of aggregates.
Currently the AggregateType equals to the FQCN of the Aggregate. To support proxies it is necessary to introduce an AggregateTypeProviderInterface. If an Aggregate implements the interface, the AggregateType is taken from the method aggregateType() instead of using the FQCN.
message_factory
and message_converter
messageToArray
, messageFromArray
Would it be possible to reduce the dependencies on ZF2? I am trying to integrate the event-store into Symfony2. Event-store depends on prooph/common which has hard dependencies on several ZF2 modules.
Right now, installing event-store drags in 7 zend modules as required dependencies
Surely these aren't all required just to use the event-store in a non-ZF2 app? Can't a bunch of these be turned into "suggests"?
With stream strategies you can add metaddata to events but currently this is only done insight a respository. Try to find ways of how this could be done from the outside. Let's say the id of the command should be added as a causation id to related events.
The command handler could pass metadata as additional argument to a repo::save method. Another idea is to have global context and use this within a special Stream Strategy.
see axon framework for inspiration: http://www.axonframework.org/docs/2.0/repositories-and-event-stores.html#event-upcasting
Update: Who needs the axon framework ๐
A stream strategy is responsible for organizing events of an aggregate in one or more streams.
Sub-Task of #19
Hi, I'm constructing my instance of AggregateRepository in this way:
class EventSourcingProjectService extends AggregateRepository implements ProjectService
{
public function __construct(EventStore $eventStore, StreamStrategyInterface $eventStoreStrategy) {
parent::__construct($eventStore, new AggregateTranslator(), $eventStoreStrategy, new AggregateType('Ora\ProjectManagement\Project'));
}
public function findProject($id)
{
$project = $this->getAggregateRoot($this->aggregateType, $id);
return $project;
}
}
but when I call findProject, I got the following error:
Argument 1 passed to Prooph\EventStore\Aggregate\AggregateRepository::getAggregateRoot() must be an instance of Prooph\EventStore\Aggregate\AggregateType, null given"
Looking at the source code, the constructor of AggregateRepository does not initialize aggregateType attribute with the value passed to. Is it correct?
Thank you very much
Andrea
You can use diffrent repos for diffrent event sourcing contracts. See (Buttercup.protects)[http://buttercup-php.github.io/protects/] as an example for such a contract.
Move the logic of extracting and populating the DomainEvents to the repository implementation.
It should be possible to configure the streaming strategy. Two options should be available:
Write docu of pros and cons of both aproaches:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.