Автор: Alexander Miertsch (contact@prooph.de)
Пример #1
0
 /**
  * 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];
 }
Пример #2
0
 /**
  * @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);
 }
Пример #3
0
 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();
 }
Пример #4
0
 /**
  * @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);
 }
Пример #9
0
 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();
 }
Пример #10
0
 /**
  * @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));
 }
Пример #16
0
 /**
  * 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());
 }
Пример #19
0
 /**
  * @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);
 }
Пример #21
0
 /**
  * @param string $aggregateRootClass
  * @param array $events
  * @return object
  */
 protected function reconstituteAggregateFromHistory($aggregateRootClass, array $events)
 {
     return $this->getAggregateTranslator()->reconstituteAggregateFromHistory(AggregateType::fromAggregateRootClass($aggregateRootClass), $events);
 }
Пример #22
0
 /**
  * @param object $eventSourcedAggregateRoot
  */
 protected function assertAggregateType($eventSourcedAggregateRoot)
 {
     $this->aggregateType->assert($eventSourcedAggregateRoot);
 }
Пример #23
0
 /**
  * @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");
 }
Пример #24
0
 /**
  * @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());
 }
Пример #27
0
 /**
  * @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);
 }
Пример #28
0
 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);
 }
Пример #29
0
        $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 "Missing todo id parameter!\n";
        exit(1);
    }
    $todoId = $argv[0];
    try {
        $todoId = TodoId::fromString($todoId);
    } catch (\Exception $ex) {
        echo "Invalid todo id given!\n";
        exit(1);
    }
    /** @var $todoList TodoList */
    $todoList = $container->get(TodoList::class);
    $todo = $todoList->get($todoId);
    if (null === $todo) {
        echo "Todo could not be found!\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 "Missing todo id parameter!\n";
        exit(1);
    }
    $todoId = $argv[0];
    try {
        $todoId = TodoId::fromString($todoId);
    } catch (\Exception $ex) {
        echo "Invalid todo id given!\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 "Todo could not be found!\n";
        exit(1);
    }
    $loadTodoEvent = $stopWatch->stop('load_todo');
    echo "Todo was loaded in " . $loadTodoEvent->getDuration() . " ms\n";
}