/** * @param Identity $sagaIdentity * @param string $sagaType * @return Saga */ public function load(Identity $sagaIdentity, string $sagaType) { $sagaData = $this->storage->findById($sagaIdentity->getValue()); if (empty($sagaData)) { return null; } return $this->serializer->deserialize($sagaData, $sagaType); }
/** * @param Identity $identity * @param EventStorage $eventStorage * @param Serializer $serializer */ private function prepareSomeEvents(Identity $identity, EventStorage $eventStorage, Serializer $serializer) { // This is not the preferred way, but used for simplicity! You should use commands to achieve this. $event = new CreatedEvent($identity); $event->setVersion(1); $eventStorage->append(EventDescriptor::record($identity->getValue(), 'aggregateType', $event->getEventName(), $serializer->serialize($event), $event->getVersion())); $event = new DemonstratedEvent($identity); $event->setVersion(2); $eventStorage->append(EventDescriptor::record($identity->getValue(), 'aggregateType', $event->getEventName(), $serializer->serialize($event), $event->getVersion())); }
/** * @return void */ public function run() { $logger = new Logger('default'); $sagaFactory = new GenericSagaFactory(); $sagaStorage = new MemorySagaStorage(); $sagaRepository = new SagaRepository($sagaStorage, new JsonSerializer()); $resolver = new SimpleAssociationValueResolver(); $sagaManager = new SimpleSagaManager([ToDoSaga::class], $sagaRepository, $resolver, $sagaFactory); $eventBus = new SimpleEventBus([]); $eventBus->setLogger(new EventLogger($logger)); $eventBus->addHandler(Event::class, $sagaManager); $eventStore = new EventStore($eventBus, new MemoryEventStorage(), new JsonSerializer(), new EventClassMap([ToDoItemCreated::class, ToDoItemDone::class, DeadlineExpired::class])); $factory = new GenericAggregateFactory(ToDoItem::class); $repository = new EventSourcingRepository($factory, $eventStore); $commandHandler = new ToDoCommandHandler($repository); $commandBus = new SimpleCommandBus([CreateToDoItem::class => $commandHandler, MarkItemDone::class => $commandHandler]); $loggingCommandInterceptor = new LoggingInterceptor(new CommandLogger($logger)); $commandGateway = new DefaultCommandGateway($commandBus, [$loggingCommandInterceptor]); $toCompleteId = Identity::createNew(); $commandGateway->send(new CreateToDoItem($toCompleteId, "Item to complete", PHP_INT_MAX)); $sagaIds = $sagaRepository->find(ToDoSaga::class, new AssociationValue('identity', $toCompleteId->getValue())); $logger->debug("Active sagas found:", ['count' => count($sagaIds), 'saga' => !empty($sagaIds) ? $sagaStorage->findById($sagaIds[0]->getValue()) : '']); $commandGateway->send(new MarkItemDone($toCompleteId)); $sagaIds = $sagaRepository->find(ToDoSaga::class, new AssociationValue('identity', $toCompleteId->getValue())); $logger->debug("Active sagas found:", ['count' => count($sagaIds), 'saga' => !empty($sagaIds) ? $sagaStorage->findById($sagaIds[0]->getValue())['serialized'] : '']); }
/** * @return void */ public function run() { $logger = new Logger('default'); $sagaId = Identity::createNew(); $toDoSaga = new ToDoSaga($sagaId, new AssociationValues([])); $eventBus = new SimpleEventBus([ToDoItemCreated::class => [$toDoSaga], ToDoItemDone::class => [$toDoSaga], DeadlineExpired::class => [$toDoSaga]]); $eventBus->setLogger(new EventLogger($logger)); $scheduler = new SimpleEventScheduler($eventBus); $toDoSaga->setEventScheduler($scheduler); $eventStore = new EventStore($eventBus, new MemoryEventStorage(), new JsonSerializer(), new EventClassMap([ToDoItemCreated::class, ToDoItemDone::class, DeadlineExpired::class])); $factory = new GenericAggregateFactory(ToDoItem::class); $repository = new EventSourcingRepository($factory, $eventStore); $commandHandler = new ToDoCommandHandler($repository); $commandBus = new SimpleCommandBus([CreateToDoItem::class => $commandHandler, MarkItemDone::class => $commandHandler]); $loggingCommandInterceptor = new LoggingInterceptor(new CommandLogger($logger)); $commandGateway = new DefaultCommandGateway($commandBus, [$loggingCommandInterceptor]); // Send commands to create two todoitems $todoItemExpireSeconds = 3; $toCompleteId = Identity::createNew(); $toExpireId = Identity::createNew(); $commandGateway->send(new CreateToDoItem($toCompleteId, "Item to complete", $todoItemExpireSeconds)); $commandGateway->send(new CreateToDoItem($toExpireId, "Item to expire", $todoItemExpireSeconds)); // Start an idling process for 5 seconds and wait for the ToDoItem to expire $this->idle(5, $commandGateway, $toCompleteId); }
/** * @param array $events * @return AggregateRoot */ private function getAggregate(array $events = []) : AggregateRoot { if ($this->aggregate == null) { $this->aggregate = $this->factory->createAggregate(Identity::createNew(), new Events($events)); } return $this->aggregate; }
/** * @test */ public function readAllRetrievesAllAnnotatedStartSagas() { $saga = new StartSagaAnnotationReaderTest_Saga(Identity::createNew(), new AssociationValues([])); $reader = new StartSagaAnnotationReader($saga); $annotations = $reader->readAll(); self::assertCount(1, $annotations); }
/** * @param Identity $identity */ public function __construct(Identity $identity = null) { if ($identity == null) { $identity = Identity::createNew(); } $this->setParameterResolver(new DefaultParameterResolver()); parent::__construct($identity, new AssociationValues([])); }
/** * @test */ public function getIdRetrievesValueOfAggregateIdentifier() { $identity = Identity::createNew(); /* @var $aggregate AnnotatedAggregateRootTest_Aggregate */ $aggregate = AnnotatedAggregateRootTest_Aggregate::reconstruct(new Events()); $aggregate->setIdentity($identity); $actualIdentity = $aggregate->getId(); self::assertEquals($identity, $actualIdentity); }
/** * @test */ public function extractAssociationValuesOnIdentity() { $identity = Identity::createNew(); $event = new SimpleAssociationValueResolverTest_Event($identity); $resolver = new SimpleAssociationValueResolver(); $associationValues = $resolver->extractAssociationValues($event); $associationValuesArray = $associationValues->getArrayCopy(); self::assertEquals($identity->getValue(), $associationValuesArray[0]->getValue()); }
/** * @test */ public function aStoryOfSubsequentEvents() { $factory = $this->createFactory(); $eventStore = $this->createEventStore(new EventClassMap([ScenarioTest_Created::class, ScenarioTest_DoneThis::class, ScenarioTest_DoneThat::class])); $repository = $this->createRepository($factory, $eventStore); $commandHandler = new ScenarioTest_CommandHandler($repository); $identity = Identity::createNew(); $scenario = new Scenario($factory, $this, $eventStore, $commandHandler); $scenario->given([new ScenarioTest_Created($identity)])->when([new ScenarioTest_DoThis($identity), new ScenarioTest_DoThat($identity)])->then([new ScenarioTest_DoneThis($identity), new ScenarioTest_DoneThat($identity)]); }
/** * @test */ public function deserializeWillHydrateDeserializedSaga() { $serializer = new JsonSerializer(); $factory = $this->createFactory(); $saga = new SagaSerializerTest_Saga(Identity::createNew(), new AssociationValues([])); $sagaType = get_class($saga); $serializedSaga = $serializer->serialize($saga); $factory->expects(self::once())->method('hydrate')->with($saga); $sagaSerializer = new SagaSerializer($serializer, $factory); $sagaSerializer->deserialize($serializedSaga, $sagaType); }
/** * @test */ public function findByIdRetrievesAggregateByIdentity() { $aggregate = new EventSourcingRepositoryTest_AggregateRoot(); $aggregateType = get_class($aggregate); $factory = $this->createAggregateFactory($aggregateType); $identity = Identity::createNew(); $events = new Events([new EventSourcingRepositoryTest_Event()]); $eventStore = $this->createEventStore(); $eventStore->expects(self::once())->method('getEventsForAggregate')->with($identity)->willReturn($events); $repository = new EventSourcingRepository($factory, $eventStore); $repository->findById($identity); }
/** * @test */ public function createNewSagaIfNoneFound() { $identity = Identity::createNew(); $event = $this->getMockBuilder(Event::class)->getMock(); $event->expects(self::any())->method('getIdentity')->willreturn($identity); $sagaTypes = [SimpleSagaManagerTest_Saga::class]; $repository = $this->createRepository(); $factory = $this->createFactory(); $resolver = $this->createAssociationValueResolver(); $resolver->expects(self::once())->method('extractAssociationValues')->willReturn(new AssociationValues([new AssociationValue('identity', $identity->getValue())])); $repository->expects(self::once())->method('find')->willReturn([]); $manager = new SimpleSagaManager($sagaTypes, $repository, $resolver, $factory); $manager->on($event); }
/** * @return void */ public function run() { $logger = new Logger('default'); $serializer = new JsonSerializer(); $stateStorage = new MemoryStateStorage(); $toDoItemProjections = new ToDoItemProjections($stateStorage); $eventStore = new EventStore(new SimpleEventBus([ToDoItemCreated::class => [$toDoItemProjections], ToDoItemDone::class => [$toDoItemProjections], DeadlineExpired::class => [$toDoItemProjections]]), new MemoryEventStorage(), $serializer, new EventClassMap([ToDoItemCreated::class, ToDoItemDone::class, DeadlineExpired::class])); $repository = new EventSourcingRepository(new GenericAggregateFactory(ToDoItem::class), $eventStore); $toDoItemCommandHandler = new ToDoCommandHandler($repository); $commandGateWay = new DefaultCommandGateway(new SimpleCommandBus([CreateToDoItem::class => $toDoItemCommandHandler, MarkItemDone::class => $toDoItemCommandHandler]), [new LoggingInterceptor(new CommandLogger($logger))]); $toDoId = Identity::createNew(); $commandGateWay->send(new CreateToDoItem($toDoId, "Wash the dishes", 5)); $logger->debug("Current state of ToDoItem", ['item' => $serializer->serialize($stateStorage->find($toDoId->getValue()))]); $commandGateWay->send(new MarkItemDone($toDoId)); $logger->debug("Current state of ToDoItem", ['item' => $serializer->serialize($stateStorage->find($toDoId->getValue()))]); }
/** * @return void */ public function run() { $logger = new Logger('default'); // The storage device to store event data $eventStorage = new MemoryEventStorage(); // A new event bus with a mapping to specify what handlers to call for what event. $eventBus = new SimpleEventBus([UserCreated::class => [new UserCreatedHandler($eventStorage, $logger)]]); $eventBus->setLogger(new EventLogger($logger)); // An event store to store events $eventStore = new EventStore($eventBus, $eventStorage, new JsonSerializer(), new EventClassMap([UserCreated::class])); // A basic aggregate factory to be able to reconstruct the aggregate $factory = new GenericAggregateFactory(User::class); // A repository for objects of type Aggregate $repository = new EventSourcingRepository($factory, $eventStore); // A new command bus with a mapping to specify what handler to call for what command. $commandBus = new SimpleCommandBus([CreateUser::class => new CreateUserHandler($repository, $logger)]); $loggingCommandInterceptor = new LoggingInterceptor(new CommandLogger($logger)); $commandGateway = new DefaultCommandGateway($commandBus, [$loggingCommandInterceptor]); // Send the command $commandGateway->send(new CreateUser(Identity::createNew())); }
/** * @return void */ public function run() { $logger = new Logger('default'); $eventBus = new SimpleEventBus(); $eventBus->setLogger(new EventLogger($logger)); $scheduler = new SimpleEventScheduler($eventBus); $parameterResolver = new DefaultParameterResolver(); $factory = new ToDoSagaFactory($scheduler, $parameterResolver, $logger); $serializer = new JsonSerializer(); $sagaManager = new SimpleSagaManager([ToDoSaga::class], new SagaRepository(new MemorySagaStorage(), new SagaSerializer($serializer, $factory)), new SimpleAssociationValueResolver(), $factory); $eventBus->addHandler(Event::class, $sagaManager); $eventStore = new EventStore($eventBus, new MemoryEventStorage(), $serializer, new EventClassMap([ToDoItemCreated::class, ToDoItemDone::class, DeadlineExpired::class])); $commandHandler = new ToDoCommandHandler(new EventSourcingRepository(new GenericAggregateFactory(ToDoItem::class), $eventStore)); $commandBus = new SimpleCommandBus([CreateToDoItem::class => $commandHandler, MarkItemDone::class => $commandHandler]); $commandGateway = new DefaultCommandGateway($commandBus, [new LoggingInterceptor(new CommandLogger($logger))]); // Send commands to create two todoitems $todoItemExpireSeconds = 3; $toCompleteId = Identity::createNew(); $commandGateway->send(new CreateToDoItem($toCompleteId, "Item to complete", $todoItemExpireSeconds)); $commandGateway->send(new CreateToDoItem(Identity::createNew(), "Item to expire", $todoItemExpireSeconds)); // Start an idling process for 5 seconds and wait for the ToDoItem to expire $this->idle(5, $commandGateway, $toCompleteId); }
/** * @param Event $event */ public function on(Event $event) { $handled = false; /* @var $sagaType string */ foreach ($this->sagaTypes as $sagaType) { $associationValues = $this->extractAssociationValues($sagaType, $event); /* @var $associationValue AssociationValue */ foreach ($associationValues->getIterator() as $associationValue) { $sagaIdentities = $this->repository->find($sagaType, $associationValue); /* @var $identity Identity */ foreach ($sagaIdentities as $identity) { $saga = $this->repository->load($identity, $sagaType); $saga->on($event); $this->commit($saga); $handled = true; } } if ($this->getSagaCreationPolicy($sagaType, $event) == SagaCreationPolicy::ALWAYS || !$handled && $this->getSagaCreationPolicy($sagaType, $event) == SagaCreationPolicy::IF_NONE_FOUND) { $saga = $this->factory->createSaga($sagaType, Identity::createNew(), $associationValues); $saga->on($event); $this->commit($saga); } } }
/** * @test * @expectedException \Apha\EventStore\AggregateNotFoundException */ public function getEventsForAggregateThrowsExceptionIfAggregateNotFound() { $eventBus = $this->getEventBus(); $storage = $this->getEventStorage(); $serializer = $this->getSerializer(); $eventMap = $this->getEventMap(); $aggregateId = Identity::createNew(); $eventStore = new EventStore($eventBus, $storage, $serializer, $eventMap); $eventStore->getEventsForAggregate($aggregateId); }
public function getId() : Identity { return Identity::createNew(); }
/** * @return Identity[] */ public function getAggregateIds() : array { return array_map(function (string $identity) : Identity { return Identity::fromString($identity); }, $this->storage->findIdentities()); }
/** * @test */ public function playToVersionPublishesEventsUpToAVersion() { $serializer = $this->getSerializer(); $eventClassMap = $this->getEventClassMap(); $eventStorage = $this->getEventStorage(); $eventBus = $this->getEventBus(); $eventStore = $this->getEventStore($eventBus, $eventStorage, $serializer, $eventClassMap); $aggregateId = Identity::createNew(); $eventVersion1 = new AggregateEventPlayerTest_Event(); $eventVersion1->setVersion(1); $eventVersion2 = new AggregateEventPlayerTest_Event(); $eventVersion2->setVersion(2); $events = new Events([$eventVersion1, $eventVersion2]); $eventStore->expects(self::any())->method('getEventsForAggregate')->with($aggregateId)->willReturn($events); $eventBus->expects(self::once())->method('publish'); $eventPlayer = new AggregateEventPlayer($eventBus, $eventStore); $eventPlayer->playToVersion($aggregateId, 1); }
/** * @param Identity $aggregateId */ public function __construct(Identity $aggregateId) { parent::__construct(sprintf(static::$messageTemplate, $aggregateId->getValue())); }
/** * @test */ public function loadReturnsNullIfSagaNotFound() { $storage = $this->createStorage(); $serializer = $this->createSerializer(); $sagaIdentity = Identity::createNew(); $storage->expects(self::once())->method('findById')->with($sagaIdentity->getValue())->willReturn(''); $repository = new SagaRepository($storage, $serializer); $actual = $repository->load($sagaIdentity, ''); self::assertNull($actual); }
/** * @test * @expectedException \InvalidArgumentException */ public function createSagaThrowsExceptionIfSagaUnsupported() { $parameterResolver = new DefaultParameterResolver(); $factory = new AnnotatedSagaFactory($parameterResolver); $factory->createSaga(\stdClass::class, Identity::createNew(), new AssociationValues([])); }
/** * @test * @expectedException \Apha\Saga\Annotation\NoStartSagaHandlerException */ public function onThrowsExceptionIfSagaCannotBeStarted() { $saga = new AnnotatedSagaTest_SagaWithoutStart(Identity::createNew()); $saga->on(new AnnotatedSagaTest_StartSaga()); }
/** * @test */ public function deleteIsIdempotent() { $sagaIdentity = Identity::createNew(); $storage = $this->createSagaStorage(); $storage->delete($sagaIdentity->getValue()); }
/** * @param int $count * @param MemoryStateStorage $storage * @return Document[] */ public function documentsProvider(int $count, MemoryStateStorage $storage) : array { $documents = []; for ($i = 0; $i < $count; $i++) { $documents[$i] = $this->getMockBuilder(Document::class)->setMethods(['getFoo', 'getBar'])->getMockForAbstractClass(); $storage->upsert(Identity::createNew()->getValue(), $documents[$i]); } return $documents; }