/** * Get the aggregate root if it exists otherwise null * * @param AggregateType $aggregateType * @param string $aggregateId * @return null|object */ public function get(AggregateType $aggregateType, $aggregateId) { if (!isset($this->map[$aggregateType->toString()][$aggregateId])) { return; } return $this->map[$aggregateType->toString()][$aggregateId]; }
/** * @param AggregateType $repositoryAggregateType * @param string $aggregateId * @param Message[] $streamEvents * @param object $aggregateRoot * @throws Exception\InvalidArgumentException * @return void */ public function appendEvents(AggregateType $repositoryAggregateType, $aggregateId, array $streamEvents, $aggregateRoot) { $arType = AggregateType::fromAggregateRoot($aggregateRoot); if (!$repositoryAggregateType->equals($arType)) { throw new Exception\InvalidArgumentException(sprintf('aggregate root mismatch between repository type %s and object type %s', $repositoryAggregateType->toString(), $arType->toString())); } $this->eventStore->appendTo($this->buildStreamName($repositoryAggregateType, $aggregateId), $streamEvents); }
protected function setUp() { parent::setUp(); $this->repository = new AggregateRepository($this->eventStore, AggregateType::fromAggregateRootClass('Prooph\\EventStoreTest\\Mock\\User'), new ConfigurableAggregateTranslator()); $this->eventStore->beginTransaction(); $this->eventStore->create(new Stream(new StreamName('event_stream'), [])); $this->eventStore->commit(); }
/** * @param TakeSnapshot $command * @throws Exception\RuntimeException */ public function __invoke(TakeSnapshot $command) { $aggregateType = $command->aggregateType(); if (!isset($this->aggregateRepositories[$aggregateType])) { throw new Exception\RuntimeException(sprintf('No repository for aggregate type %s configured', $command->aggregateType())); } $repository = $this->aggregateRepositories[$aggregateType]; $aggregateRoot = $repository->getAggregateRoot($command->aggregateId()); if (null === $aggregateRoot) { throw new RuntimeException(sprintf('Could not find aggregate root %s with id %s', $aggregateType, $command->aggregateId())); } $this->snapshotStore->save(new Snapshot(AggregateType::fromAggregateRootClass($aggregateType), $command->aggregateId(), $aggregateRoot, $repository->extractAggregateVersion($aggregateRoot), $command->createdAt())); }
/** * @test */ public function an_invokable_plugin_is_loaded_by_plugin_manager_and_attached_to_event_store_by_configuration() { $pluginManager = new ServiceManager(new Config(["invokables" => ["eventlogger" => EventLoggerPlugin::class]])); $eventStore = new EventStore(new InMemoryAdapter(), new ProophActionEventEmitter()); $logger = $pluginManager->get('eventlogger'); $logger->setUp($eventStore); $repository = new AggregateRepository($eventStore, AggregateType::fromAggregateRootClass('ProophTest\\EventStore\\Mock\\User'), new ConfigurableAggregateTranslator(), null, null, true); $eventStore->beginTransaction(); $user = User::create("Alex", "*****@*****.**"); $repository->addAggregateRoot($user); $eventStore->commit(); $loggedStreamEvents = $pluginManager->get("eventlogger")->getLoggedStreamEvents(); $this->assertEquals(1, count($loggedStreamEvents)); }
/** * @test */ public function it_uses_custom_snapshot_grid_fs_map_and_write_concern() { $this->adapter = new MongoDbAdapter($this->client, 'test', ['w' => 'majority', 'j' => true], ['foo' => 'bar']); $aggregateType = AggregateType::fromString('foo'); $aggregateRoot = new \stdClass(); $aggregateRoot->foo = 'bar'; $time = microtime(true); if (false === strpos($time, '.')) { $time .= '.0000'; } $now = \DateTimeImmutable::createFromFormat('U.u', $time); $snapshot = new Snapshot($aggregateType, 'id', $aggregateRoot, 1, $now); $this->adapter->add($snapshot); $gridFs = $this->client->selectDB('test')->getGridFS('bar'); $file = $gridFs->findOne(); $this->assertNotNull($file); }
/** * @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']); $snapshotStore = isset($config['snapshot_store']) ? $container->get($config['snapshot_store']) : null; $streamName = isset($config['stream_name']) ? new StreamName($config['stream_name']) : null; $oneStreamPerAggregate = isset($config['one_stream_per_aggregate']) ? (bool) $config['one_stream_per_aggregate'] : false; return new $repositoryClass($eventStore, $aggregateType, $aggregateTranslator, $snapshotStore, $streamName, $oneStreamPerAggregate); }
/** * @test */ public function it_saves_and_reads() { $m = new \Memcached(); $m->addServer('localhost', 11211); $adapter = new MemcachedSnapshotAdapter($m); $aggregateType = AggregateType::fromString('foo'); $aggregateRoot = new \stdClass(); $aggregateRoot->foo = 'bar'; $time = microtime(true); if (false === strpos($time, '.')) { $time .= '.0000'; } $now = \DateTimeImmutable::createFromFormat('U.u', $time, new \DateTimeZone('UTC')); $snapshot = new Snapshot($aggregateType, 'id', $aggregateRoot, 1, $now); $adapter->save($snapshot); $this->assertNull($adapter->get($aggregateType, 'invalid')); $readSnapshot = $adapter->get($aggregateType, 'id'); $this->assertEquals($snapshot, $readSnapshot); }
public function setUp() { $inMemoryAdapter = new InMemoryAdapter(); $eventEmitter = new ProophActionEventEmitter(); $this->eventStore = new EventStore($inMemoryAdapter, $eventEmitter); $this->repository = new AggregateRepository($this->eventStore, AggregateType::fromAggregateRootClass('ProophTest\\EventStore\\Mock\\User'), new ConfigurableAggregateTranslator()); $this->result = []; $self = $this; $router = new CommandRouter(); $router->route(TakeSnapshot::class)->to(function (TakeSnapshot $command) use($self) { $self->result[] = ['aggregate_type' => $command->aggregateType(), 'aggregate_id' => $command->aggregateId()]; }); $commandBus = new CommandBus(); $commandBus->utilize($router); $plugin = new SnapshotPlugin($commandBus, 2); $plugin->setUp($this->eventStore); $this->eventStore->beginTransaction(); $this->eventStore->create(new Stream(new StreamName('event_stream'), new \ArrayIterator())); $this->eventStore->commit(); }
/** * @test */ public function it_publishes_take_snapshot_commands_for_all_known_aggregates() { $inMemoryAdapter = new InMemoryAdapter(); $eventEmitter = new ProophActionEventEmitter(); $eventStore = new EventStore($inMemoryAdapter, $eventEmitter); $repository = new AggregateRepository($eventStore, AggregateType::fromAggregateRootClass('ProophTest\\EventStore\\Mock\\User'), new ConfigurableAggregateTranslator()); $result = []; $router = new CommandRouter(); $router->route(TakeSnapshot::class)->to(function (TakeSnapshot $command) use(&$result) { $result[] = ['aggregate_type' => $command->aggregateType(), 'aggregate_id' => $command->aggregateId()]; }); $commandBus = new CommandBus(); $commandBus->utilize($router); $plugin = new SnapshotPlugin($commandBus, 2); $plugin->setUp($eventStore); $eventStore->beginTransaction(); $eventStore->create(new Stream(new StreamName('event_stream'), new \ArrayIterator())); $eventStore->commit(); $eventStore->beginTransaction(); $user = User::create('Alex', '*****@*****.**'); $repository->addAggregateRoot($user); $eventStore->commit(); $eventStore->beginTransaction(); $user = $repository->getAggregateRoot($user->getId()->toString()); $user->changeName('John'); $user->changeName('Jane'); $user->changeName('Jim'); $eventStore->commit(); $eventStore->beginTransaction(); $eventWithoutMetadata1 = UsernameChanged::with(['new_name' => 'John Doe'], 5); $eventWithoutMetadata2 = UsernameChanged::with(['new_name' => 'Jane Doe'], 6); $eventStore->appendTo(new StreamName('event_stream'), new \ArrayIterator([$eventWithoutMetadata1, $eventWithoutMetadata2])); $eventStore->commit(); $this->assertCount(2, $result); $this->assertArrayHasKey('aggregate_type', $result[0]); $this->assertArrayHasKey('aggregate_id', $result[0]); $this->assertArrayHasKey('aggregate_type', $result[1]); $this->assertArrayHasKey('aggregate_id', $result[1]); $this->assertEquals(User::class, $result[0]['aggregate_type']); $this->assertEquals(User::class, $result[1]['aggregate_type']); }
/** * @test */ public function it_uses_custom_snapshot_table_map() { $schema = new Schema(); SnapshotStoreSchema::create($schema, 'bar'); foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) { $this->connection->executeQuery($sql); } $adapter = new DoctrineSnapshotAdapter($this->connection, ['foo' => 'bar']); $aggregateType = AggregateType::fromString('foo'); $aggregateRoot = new \stdClass(); $aggregateRoot->foo = 'bar'; $time = microtime(true); if (false === strpos($time, '.')) { $time .= '.0000'; } $now = \DateTimeImmutable::createFromFormat('U.u', $time); $snapshot = new Snapshot($aggregateType, 'id', $aggregateRoot, 1, $now); $adapter->add($snapshot); $queryBuilder = $this->connection->createQueryBuilder(); $queryBuilder->select('*')->from('bar', 'bar')->setMaxResults(1); $stmt = $queryBuilder->execute(); $this->assertNotNull($stmt->fetch(\PDO::FETCH_ASSOC)); }
/** * @test */ public function it_uses_callback_to_convert_message_into_custom_domain_event() { $historyEvent = $this->prophesize(Message::class); $historyEvents = [$historyEvent->reveal()]; $translator = new ConfigurableAggregateTranslator(null, null, null, null, function (Message $message) { return ['custom' => 'domainEvent']; }); $ar = $translator->reconstituteAggregateFromHistory(AggregateType::fromAggregateRootClass(DefaultAggregateRoot::class), $historyEvents); $this->assertEquals([['custom' => 'domainEvent']], $ar->getHistoryEvents()); }
/** * @param AggregateType $aggregateType * @param \Iterator $historyEvents * @return object reconstructed AggregateRoot */ public function reconstituteAggregateFromHistory(AggregateType $aggregateType, \Iterator $historyEvents) { return $this->getAggregateRootDecorator()->fromHistory($aggregateType->toString(), $historyEvents); }
/** * @param EventStore $eventStore */ public function __construct(EventStore $eventStore) { parent::__construct($eventStore, new AggregateTranslator(), new SingleStreamStrategy($eventStore, 'link_process_manager_stream'), AggregateType::fromAggregateRootClass('Prooph\\Link\\ProcessManager\\Model\\MessageHandler')); }
/** * @param AggregateType $aggregateType * @return string */ private function getShortAggregateTypeName(AggregateType $aggregateType) { $aggregateTypeName = str_replace('-', '_', $aggregateType->toString()); return implode('', array_slice(explode('\\', $aggregateTypeName), -1)); }
/** * Add aggregate_id and aggregate_type as metadata to $domainEvent * Override this method in an extending repository to add more or different metadata. * * @param Message $domainEvent * @param string $aggregateId * @return Message */ protected function enrichEventMetadata(Message $domainEvent, $aggregateId) { $domainEvent = $domainEvent->withAddedMetadata('aggregate_id', $aggregateId); return $domainEvent->withAddedMetadata('aggregate_type', $this->aggregateType->toString()); }
/** * @test * @expectedException \InvalidArgumentException */ public function it_does_not_allow_to_add_stream_events_with_wrong_repository_aggregate_type() { $this->eventStore->beginTransaction(); $user = User::create("John Doe", "*****@*****.**"); $aggregateType = AggregateType::fromAggregateRoot($user); $aggregateId = Uuid::uuid4()->toString(); $streamEvents = [UserCreated::with(['user_id' => $aggregateId], 1)]; $this->strategy->addEventsForNewAggregateRoot($aggregateType, $aggregateId, $streamEvents, $user); $this->eventStore->commit(); $this->eventStore->beginTransaction(); $streamEvents = [UsernameChanged::with(['name' => 'John Doe'], 2)]; $this->strategy->appendEvents(AggregateType::fromString("Product"), $aggregateId, $streamEvents, $user); }
/** * @param ContainerInterface $container * @return EventStoreTodoList */ public function __invoke(ContainerInterface $container) { return new EventStoreTodoList($container->get('prooph.event_store'), AggregateType::fromAggregateRootClass(Todo::class), new AggregateTranslator()); }
/** * @param EventStore $eventStore * @param string $storyLogStreamName * @param SnapshotStore $snapshotStore */ public function __construct(EventStore $eventStore, $storyLogStreamName = 'prooph_story_stream', SnapshotStore $snapshotStore = null) { parent::__construct($eventStore, AggregateType::fromAggregateRootClass(Story::class), new AggregateTranslator(), new SingleStreamStrategy($eventStore, $storyLogStreamName), $snapshotStore); }
/** * @test */ public function it_deals_with_resources_on_serialized_aggregate_roots() { /** @var Connection|\PHPUnit_Framework_MockObject_MockObject $connection */ $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); /** @var QueryBuilder|\PHPUnit_Framework_MockObject_MockObject $queryBuilder */ $queryBuilder = $this->getMockBuilder(QueryBuilder::class)->disableOriginalConstructor()->getMock(); /** @var Statement|\PHPUnit_Framework_MockObject_MockObject $stmt */ $stmt = $this->getMockForAbstractClass(Statement::class); $aggregateRoot = new \stdClass(); $aggregateRoot->data = 'AggregateRoot'; $resource = fopen('php://temp', 'r+'); fwrite($resource, serialize($aggregateRoot)); fseek($resource, 0); $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); $queryBuilder->expects($this->once())->method('select')->willReturnSelf(); $queryBuilder->expects($this->once())->method('from')->willReturnSelf(); $queryBuilder->expects($this->any())->method('where')->willReturnSelf(); $queryBuilder->expects($this->any())->method('andWhere')->willReturnSelf(); $queryBuilder->expects($this->any())->method('orderBy')->willReturnSelf(); $queryBuilder->expects($this->any())->method('setParameter')->willReturnSelf(); $queryBuilder->expects($this->any())->method('setMaxResults')->willReturnSelf(); $queryBuilder->expects($this->once())->method('execute')->willReturn($stmt); $stmt->expects($this->once())->method('fetch')->with(\PDO::FETCH_ASSOC)->willReturn(['aggregate_root' => $resource, 'last_version' => 3, 'created_at' => '2016-01-21T09:33:00.000']); $adapter = new DoctrineSnapshotAdapter($connection, ['foo' => 'bar']); $snapshot = $adapter->get(AggregateType::fromString('foo'), 'some-uuid-non-important-here'); $this->assertEquals('AggregateRoot', $snapshot->aggregateRoot()->data); }
/** * @param string $aggregateRootClass * @param array $events * @return object */ protected function reconstituteAggregateFromHistory($aggregateRootClass, array $events) { return $this->getAggregateTranslator()->reconstituteAggregateFromHistory(AggregateType::fromAggregateRootClass($aggregateRootClass), $events); }
/** * @param object $eventSourcedAggregateRoot */ protected function assertAggregateType($eventSourcedAggregateRoot) { $this->aggregateType->assert($eventSourcedAggregateRoot); }
/** * @param AggregateType $repositoryAggregateType * @param Message[] $streamEvents * @throws Exception\RuntimeException * @return AggregateType */ public function getAggregateRootType(AggregateType $repositoryAggregateType, array &$streamEvents) { if (count($streamEvents)) { $first = $streamEvents[0]; $metadata = $first->metadata(); if (isset($metadata['aggregate_type'])) { return AggregateType::fromAggregateRootClass($metadata['aggregate_type']); } } throw new Exception\RuntimeException("The aggregate type cannot be detected"); }
/** * @test */ public function it_delegates_to_string() { $type = AggregateType::fromAggregateRootClass('stdClass'); $this->assertEquals('stdClass', (string) $type); }
/** * @param AggregateType $aggregateType * @param Iterator $historyEvents * @throws Exception\AggregateTranslationFailedException * @return object reconstructed EventSourcedAggregateRoot */ public function reconstituteAggregateFromHistory(AggregateType $aggregateType, Iterator $historyEvents) { if ($this->messageToEventCallback) { $historyEvents = new MapIterator($historyEvents, $this->messageToEventCallback); } $aggregateClass = $aggregateType->toString(); if (!class_exists($aggregateClass)) { throw new AggregateTranslationFailedException(sprintf('Can not reconstitute aggregate of type %s. Class was not found', $aggregateClass)); } if (!method_exists($aggregateClass, $this->staticReconstituteFromHistoryMethodName)) { throw new AggregateTranslationFailedException(sprintf('Cannot reconstitute aggregate of type %s. Class is missing a static %s method!', $aggregateClass, $this->staticReconstituteFromHistoryMethodName)); } $method = $this->staticReconstituteFromHistoryMethodName; $aggregate = $aggregateClass::$method($historyEvents); if (!$aggregate instanceof $aggregateClass) { throw new AggregateTranslationFailedException(sprintf('Failed to reconstitute aggregate of type %s. Static method %s does not return an instance of the aggregate type!', $aggregateClass, $this->staticReconstituteFromHistoryMethodName)); } return $aggregate; }
protected function resetRepository() { $this->repository = new AggregateRepository($this->eventStore, AggregateType::fromAggregateRootClass('ProophTest\\EventSourcing\\Mock\\User'), new AggregateTranslator()); }
/** * @param EventStore $eventStore */ public function __construct(EventStore $eventStore) { $aggregateType = AggregateType::fromAggregateRootClass('Prooph\\Processing\\Processor\\Process'); parent::__construct($eventStore, new AggregateTranslator(), new SingleStreamStrategy($eventStore, 'prooph_processing_stream'), $aggregateType); }
public function __construct(EventStore $eventStore) { //We inject a Prooph\EventSourcing\EventStoreIntegration\AggregateTranslator that can handle our AggregateRoots parent::__construct($eventStore, AggregateType::fromAggregateRootClass('My\\Model\\User'), new AggregateTranslator(), null, null, true); }
$todoReflected = new \ReflectionClass($todo); $versionProp = $todoReflected->getProperty('version'); $versionProp->setAccessible(true); return $versionProp->getValue($todo); } $container = (require 'config/container.php'); array_shift($argv); if (empty($argv)) { echo "[1;31mMissing todo id parameter![0m\n"; exit(1); } $todoId = $argv[0]; try { $todoId = TodoId::fromString($todoId); } catch (\Exception $ex) { echo "[1;31mInvalid todo id given![0m\n"; exit(1); } /** @var $todoList TodoList */ $todoList = $container->get(TodoList::class); $todo = $todoList->get($todoId); if (null === $todo) { echo "[1;31mTodo could not be found![0m\n"; exit(1); } /** @var $snapshotStore SnapshotStore */ $snapshotStore = $container->get(SnapshotStore::class); $snapshot = new Snapshot(AggregateType::fromAggregateRoot($todo), $todoId->toString(), $todo, get_todo_version($todo), new \DateTimeImmutable("now", new \DateTimeZone('UTC'))); $snapshotStore->save($snapshot); echo "Snapshot was taken!\n"; }
use Prooph\ProophessorDo\Model\Todo\TodoId; use Symfony\Component\Stopwatch\Stopwatch; chdir(dirname(__DIR__)); // Setup autoloading require 'vendor/autoload.php'; $container = (require 'config/container.php'); array_shift($argv); if (empty($argv)) { echo "[1;31mMissing todo id parameter![0m\n"; exit(1); } $todoId = $argv[0]; try { $todoId = TodoId::fromString($todoId); } catch (\Exception $ex) { echo "[1;31mInvalid todo id given![0m\n"; exit(1); } //Set up todo repository by hand to make sure that no snapshot store is used $eventStore = $container->get(EventStore::class); $todoList = new EventStoreTodoList($eventStore, AggregateType::fromAggregateRootClass(Todo::class), new AggregateTranslator(), $container->get(SnapshotStore::class)); $stopWatch = new Stopwatch(); $stopWatch->start('load_todo'); $todo = $todoList->get($todoId); if (null === $todo) { echo "[1;31mTodo could not be found![0m\n"; exit(1); } $loadTodoEvent = $stopWatch->stop('load_todo'); echo "Todo was loaded in [1m" . $loadTodoEvent->getDuration() . "[0m ms\n"; }