From 566cc809801ca2607d4e74f0adf4afa142732fa3 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 9 Apr 2021 16:43:03 +0200 Subject: [PATCH 01/18] Add missing types --- composer.json | 2 +- src/BoundedContext.php | 29 +++++++ src/Document.php | 4 +- src/EventSourcingAnalyzer.php | 123 ++++++++++++++++++++++++++-- src/ExternalSystem.php | 29 +++++++ src/Feature.php | 29 +++++++ src/HotSpot.php | 29 +++++++ src/Policy.php | 29 +++++++ src/Ui.php | 29 +++++++ src/Vertex.php | 12 +++ tests/EventSourcingAnalyzerTest.php | 6 +- 11 files changed, 309 insertions(+), 12 deletions(-) create mode 100644 src/BoundedContext.php create mode 100644 src/ExternalSystem.php create mode 100644 src/Feature.php create mode 100644 src/HotSpot.php create mode 100644 src/Policy.php create mode 100644 src/Ui.php diff --git a/composer.json b/composer.json index 47be072..68caa68 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^7.3 || ^8.0", - "event-engine/php-inspectio-graph": "^0.3.0", + "event-engine/php-inspectio-graph": "dev-feature/add-missing-types", "ext-json": "*" }, "require-dev": { diff --git a/src/BoundedContext.php b/src/BoundedContext.php new file mode 100644 index 0000000..7d8051b --- /dev/null +++ b/src/BoundedContext.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Document.php b/src/Document.php index c668b74..b648369 100644 --- a/src/Document.php +++ b/src/Document.php @@ -10,10 +10,10 @@ namespace EventEngine\InspectioGraphCody; -use EventEngine\InspectioGraph\EventType; +use EventEngine\InspectioGraph\DocumentType; use EventEngine\InspectioGraph\Metadata; -final class Document extends Vertex implements EventType +final class Document extends Vertex implements DocumentType { protected const TYPE = self::TYPE_DOCUMENT; diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index a9e0a76..40ebd01 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -10,13 +10,14 @@ namespace EventEngine\InspectioGraphCody; -use EventEngine\InspectioGraph\AggregateConnection; -use EventEngine\InspectioGraph\AggregateConnectionMap; +use EventEngine\InspectioGraph; +use EventEngine\InspectioGraph\Connection\AggregateConnection; +use EventEngine\InspectioGraph\Connection\AggregateConnectionMap; use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraph\VertexType; use EventEngine\InspectioGraphCody\Exception\RuntimeException; -final class EventSourcingAnalyzer implements \EventEngine\InspectioGraph\EventSourcingAnalyzer +final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, InspectioGraph\Connection\AggregateConnectionAnalyzer { /** * @var Node @@ -33,6 +34,16 @@ final class EventSourcingAnalyzer implements \EventEngine\InspectioGraph\EventSo */ private $commandMap; + /** + * @var VertexMap + */ + private $aggregateMap; + + /** + * @var AggregateConnectionMap + */ + private $aggregateConnectionMap; + /** * @var VertexMap */ @@ -44,9 +55,34 @@ final class EventSourcingAnalyzer implements \EventEngine\InspectioGraph\EventSo private $documentMap; /** - * @var AggregateConnectionMap + * @var VertexMap */ - private $aggregateConnectionMap; + private $policyMap; + + /** + * @var VertexMap + */ + private $uiMap; + + /** + * @var VertexMap + */ + private $externalSystemMap; + + /** + * @var VertexMap + */ + private $hotSpotMap; + + /** + * @var VertexMap + */ + private $featureMap; + + /** + * @var VertexMap + */ + private $boundedContextMap; /** * @var callable @@ -75,6 +111,12 @@ private function filterVerticesByType(string $type): array $vertices[] = $this->node; } + $parent = $this->node->parent(); + + if ($parent && $parent->type() === $type) { + $vertices[] = $parent; + } + foreach ($this->node->sources() as $source) { if ($source->type() === $type) { $vertices[] = $source; @@ -87,6 +129,12 @@ private function filterVerticesByType(string $type): array } } + foreach ($this->node->children() as $child) { + if ($child->type() === $type) { + $vertices[] = $child; + } + } + return $vertices; } @@ -173,7 +221,16 @@ public function eventMap(): VertexMap return $this->eventMap; } - public function aggregateMap(): AggregateConnectionMap + public function aggregateMap(): VertexMap + { + if (null === $this->aggregateMap) { + $this->aggregateMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_AGGREGATE)); + } + + return $this->aggregateMap; + } + + public function aggregateConnectionMap(): AggregateConnectionMap { if (null === $this->aggregateConnectionMap) { $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); @@ -262,4 +319,58 @@ private function areNodesEqual(Node $a, Node $b): bool return $a->name() === $b->name() && $a->type() === $b->type(); } + + public function policyMap(): VertexMap + { + if (null === $this->policyMap) { + $this->policyMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_POLICY)); + } + + return $this->policyMap; + } + + public function uiMap(): VertexMap + { + if (null === $this->uiMap) { + $this->uiMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_UI)); + } + + return $this->uiMap; + } + + public function featureMap(): VertexMap + { + if (null === $this->featureMap) { + $this->featureMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_FEATURE)); + } + + return $this->featureMap; + } + + public function boundedContextMap(): VertexMap + { + if (null === $this->boundedContextMap) { + $this->boundedContextMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_BOUNDED_CONTEXT)); + } + + return $this->boundedContextMap; + } + + public function externalSystemMap(): VertexMap + { + if (null === $this->externalSystemMap) { + $this->externalSystemMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_EXTERNAL_SYSTEM)); + } + + return $this->externalSystemMap; + } + + public function hotSpotMap(): VertexMap + { + if (null === $this->hotSpotMap) { + $this->hotSpotMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_HOT_SPOT)); + } + + return $this->hotSpotMap; + } } diff --git a/src/ExternalSystem.php b/src/ExternalSystem.php new file mode 100644 index 0000000..e11c7de --- /dev/null +++ b/src/ExternalSystem.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Feature.php b/src/Feature.php new file mode 100644 index 0000000..ba8b649 --- /dev/null +++ b/src/Feature.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/HotSpot.php b/src/HotSpot.php new file mode 100644 index 0000000..4fe8824 --- /dev/null +++ b/src/HotSpot.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Policy.php b/src/Policy.php new file mode 100644 index 0000000..fee96a8 --- /dev/null +++ b/src/Policy.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Ui.php b/src/Ui.php new file mode 100644 index 0000000..69b72a3 --- /dev/null +++ b/src/Ui.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Vertex.php b/src/Vertex.php index 2e6d716..92d4d64 100644 --- a/src/Vertex.php +++ b/src/Vertex.php @@ -69,6 +69,18 @@ public static function fromCodyNode( case VertexType::TYPE_DOCUMENT: $class = Document::class; break; + case VertexType::TYPE_POLICY: + $class = Policy::class; + break; + case VertexType::TYPE_UI: + $class = Ui::class; + break; + case VertexType::TYPE_FEATURE: + $class = Feature::class; + break; + case VertexType::TYPE_BOUNDED_CONTEXT: + $class = BoundedContext::class; + break; default: throw new RuntimeException(\sprintf('Given type "%s" is not supported', $type)); } diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index 00c83b5..7e9cd54 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -57,7 +57,7 @@ public function it_returns_aggregate_map_of_command_node(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); - $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); $aggregate = $aggregateMap->current(); @@ -79,7 +79,7 @@ public function it_returns_aggregate_map_of_event_node(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); - $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); $aggregate = $aggregateMap->current(); @@ -136,7 +136,7 @@ public function it_returns_aggregate_map_of_aggregate_node(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); - $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); $aggregate = $aggregateMap->current(); From 7926c034a67f1c87a66f097e47d51ef309413874 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 16 Apr 2021 09:07:02 +0200 Subject: [PATCH 02/18] Implement FeatureConnectionAnalyzer --- phpstan.neon.dist | 2 + src/EventSourcingAnalyzer.php | 287 ++- tests/EventSourcingAnalyzerTest.php | 187 +- tests/JsonNodeTest.php | 25 +- tests/_files/add_building.json | 86 +- tests/_files/building.json | 156 +- tests/_files/building_added.json | 125 +- tests/_files/building_user.json | 204 +- tests/_files/feature_building.json | 3459 +++++++++++++++++++++++++++ 9 files changed, 4257 insertions(+), 274 deletions(-) create mode 100644 tests/_files/feature_building.json diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 9bd5aa7..5ff44e5 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,3 +2,5 @@ parameters: level: 5 paths: - src/ + ignoreErrors: + - '#.*expects EventEngine\\InspectioGraph\\[a-zA-Z0-9\\_]+\, EventEngine\\InspectioGraph\\VertexType given.#' diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index 40ebd01..5f2d1dd 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -12,12 +12,16 @@ use EventEngine\InspectioGraph; use EventEngine\InspectioGraph\Connection\AggregateConnection; +use EventEngine\InspectioGraph\Connection\AggregateConnectionAnalyzer; use EventEngine\InspectioGraph\Connection\AggregateConnectionMap; +use EventEngine\InspectioGraph\Connection\FeatureConnection; +use EventEngine\InspectioGraph\Connection\FeatureConnectionAnalyzer; +use EventEngine\InspectioGraph\Connection\FeatureConnectionMap; use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraph\VertexType; use EventEngine\InspectioGraphCody\Exception\RuntimeException; -final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, InspectioGraph\Connection\AggregateConnectionAnalyzer +final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, AggregateConnectionAnalyzer, FeatureConnectionAnalyzer { /** * @var Node @@ -79,6 +83,11 @@ final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyze */ private $featureMap; + /** + * @var FeatureConnectionMap + */ + private $featureConnectionMap; + /** * @var VertexMap */ @@ -139,60 +148,86 @@ private function filterVerticesByType(string $type): array } /** - * @param Node $node - * @return Node[] + * @param string $vertexType + * @param VertexType $node + * @return VertexMap */ - private function filterCommandsWithConnectionOf(Node $node): array + private function filterVertexTypeWithConnectionOf(string $vertexType, VertexType $node): VertexMap { - $vertices = []; + $vertices = VertexMap::emptyMap(); switch ($this->node->type()) { case VertexType::TYPE_AGGREGATE: foreach ($this->node->sources() as $source) { - if ($source->type() === VertexType::TYPE_COMMAND) { - $vertices[] = $source; + if ($source->type() === $vertexType + && ($vertex = $this->vertexFromMap($source)) + ) { + $vertices = $vertices->with($vertex); } } - break; - case VertexType::TYPE_COMMAND: foreach ($this->node->targets() as $target) { - if ($this->areNodesEqual($target, $node)) { - $vertices[] = $this->node; + if ($target->type() === $vertexType + && ($vertex = $this->vertexFromMap($target)) + ) { + $vertices = $vertices->with($vertex); } } - break; - default: - break; - } + $parent = $this->node->parent(); - return $vertices; - } - - /** - * @param Node $node - * @return Node[] - */ - private function filterEventsWithConnectionOf(Node $node): array - { - $vertices = []; - - switch ($this->node->type()) { - case VertexType::TYPE_AGGREGATE: - foreach ($this->node->targets() as $target) { - if ($target->type() === VertexType::TYPE_EVENT) { - $vertices[] = $target; - } + if (null !== $parent + && $this->node->type() === $vertexType + && $this->areNodesEqual($parent, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($this->node)); } break; + case VertexType::TYPE_COMMAND: case VertexType::TYPE_EVENT: foreach ($this->node->sources() as $source) { - if ($this->areNodesEqual($source, $node)) { - $vertices[] = $this->node; + if ($this->node->type() === $vertexType + && $this->areNodesEqual($source, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($this->node)); + } elseif ($parent = $source->parent()) { + if ($source->type() === $vertexType + && $this->areNodesEqual($parent, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($source)); + } } } foreach ($this->node->targets() as $target) { - if ($this->areNodesEqual($target, $node)) { - $vertices[] = $this->node; + if ($this->node->type() === $vertexType + && $this->areNodesEqual($target, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($this->node)); + } elseif ($parent = $target->parent()) { + if ($target->type() === $vertexType + && $this->areNodesEqual($parent, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($target)); + } + } + } + $parent = $this->node->parent(); + + if (null !== $parent + && $this->node->type() === $vertexType + && $this->areNodesEqual($parent, $node) + ) { + $vertices = $vertices->with($this->vertexFromMap($this->node)); + } + break; + case VertexType::TYPE_FEATURE: + foreach ($this->node->children() as $child) { + if ($child->type() === $vertexType + && ($vertex = $this->vertexFromMap($child)) + ) { + if ($node instanceof InspectioGraph\FeatureType) { + $vertices = $vertices->with($vertex); + } elseif ($this->isFeatureVertexSourceOrTargetOf($vertex, $node)) { + $vertices = $vertices->with($vertex); + } } } break; @@ -203,6 +238,30 @@ private function filterEventsWithConnectionOf(Node $node): array return $vertices; } + private function isFeatureVertexSourceOrTargetOf(VertexType $vertexType, VertexType $node): bool + { + foreach ($this->node->children() as $child) { + if ($child->type() === 'edge' + || $this->areNodesIdentical($child, $node) === false + ) { + continue; + } + + foreach ($child->sources() as $childSource) { + if ($this->areNodesIdentical($childSource, $vertexType)) { + return true; + } + } + foreach ($child->targets() as $childTarget) { + if ($this->areNodesIdentical($childTarget, $vertexType)) { + return true; + } + } + } + + return false; + } + public function commandMap(): VertexMap { if (null === $this->commandMap) { @@ -235,23 +294,16 @@ public function aggregateConnectionMap(): AggregateConnectionMap if (null === $this->aggregateConnectionMap) { $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - $commandMap = $this->commandMap(); - $eventMap = $this->eventMap(); - - /** @var Node $aggregateVertex */ - foreach ($this->filterVerticesByType(VertexType::TYPE_AGGREGATE) as $aggregateVertex) { - $aggregate = Vertex::fromCodyNode($aggregateVertex, $this->filterName, $this->metadataFactory); + foreach ($this->filterVerticesByType(VertexType::TYPE_AGGREGATE) as $node) { + $aggregate = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); $name = $aggregate->name(); - if (true === $this->aggregateConnectionMap->has($name)) { - continue; - } // @phpstan-ignore-next-line $aggregateConnection = new AggregateConnection($aggregate); - $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($name, $aggregateConnection); - $commandVertices = $this->filterCommandsWithConnectionOf($aggregateVertex); - $eventVertices = $this->filterEventsWithConnectionOf($aggregateVertex); + $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); + $commandVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $aggregate); + $eventVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $aggregate); $countCommandVertices = \count($commandVertices); @@ -262,33 +314,14 @@ public function aggregateConnectionMap(): AggregateConnectionMap } if ($countCommandVertices === 1) { - $command = Vertex::fromCodyNode(\current($commandVertices), $this->filterName); - - if (true === $commandMap->has($command->name())) { - $events = []; - - foreach ($eventVertices as $eventVertex) { - $event = Vertex::fromCodyNode($eventVertex, $this->filterName); - - if ($eventMap->has($event->name())) { - $events[] = $eventMap->vertex($event->name()); - } - } - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withCommandEvents($commandMap->vertex($command->name()), ...$events); - } + $command = $commandVertices->current(); + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); } elseif (\count($eventVertices) > 0) { - foreach ($eventVertices as $eventVertex) { - $events = []; - $event = Vertex::fromCodyNode($eventVertex, $this->filterName); - - if ($eventMap->has($event->name())) { - $events[] = $eventMap->vertex($event->name()); - } - } - $aggregateConnection = $aggregateConnection->withEvents(...$events); + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); } - $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($name, $aggregateConnection); + $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); } } @@ -314,12 +347,86 @@ function (Node $vertex) { ); } - private function areNodesEqual(Node $a, Node $b): bool + private function vertexFromMap(Node $node): ?VertexType { - return $a->name() === $b->name() + $name = ($this->filterName)($node->name()); + + switch ($node->type()) { + case VertexType::TYPE_COMMAND: + if ($this->commandMap()->has($name)) { + return $this->commandMap()->vertex($name); + } + + return null; + case VertexType::TYPE_AGGREGATE: + if ($this->aggregateMap()->has($name)) { + return $this->aggregateMap()->vertex($name); + } + + return null; + case VertexType::TYPE_EVENT: + if ($this->eventMap()->has($name)) { + return $this->eventMap()->vertex($name); + } + + return null; + case VertexType::TYPE_DOCUMENT: + if ($this->documentMap()->has($name)) { + return $this->documentMap()->vertex($name); + } + + return null; + case VertexType::TYPE_POLICY: + if ($this->policyMap()->has($name)) { + return $this->policyMap()->vertex($name); + } + + return null; + case VertexType::TYPE_UI: + if ($this->uiMap()->has($name)) { + return $this->uiMap()->vertex($name); + } + + return null; + case VertexType::TYPE_HOT_SPOT: + if ($this->hotSpotMap()->has($name)) { + return $this->hotSpotMap()->vertex($name); + } + + return null; + case VertexType::TYPE_FEATURE: + if ($this->featureMap()->has($name)) { + return $this->featureMap()->vertex($name); + } + + return null; + case VertexType::TYPE_BOUNDED_CONTEXT: + if ($this->boundedContextMap()->has($name)) { + return $this->boundedContextMap()->vertex($name); + } + + return null; + default: + throw new RuntimeException( + \sprintf('Type "%s" is not supported', $node->type()) + ); + } + } + + private function areNodesEqual(Node $a, VertexType $b): bool + { + $nodeName = ($this->filterName)($a->name()); + + return $nodeName === $b->name() && $a->type() === $b->type(); } + private function areNodesIdentical(Node $a, VertexType $b): bool + { + return $a->type() === $b->type() + && $a->id() === $b->id(); + } + public function policyMap(): VertexMap { if (null === $this->policyMap) { @@ -347,6 +454,36 @@ public function featureMap(): VertexMap return $this->featureMap; } + public function featureConnectionMap(): FeatureConnectionMap + { + if (null === $this->featureConnectionMap) { + $this->featureConnectionMap = FeatureConnectionMap::emptyMap(); + + foreach ($this->filterVerticesByType(VertexType::TYPE_FEATURE) as $node) { + $feature = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); + + // @phpstan-ignore-next-line + $featureConnection = new FeatureConnection($feature); + + $this->featureConnectionMap = $this->featureConnectionMap->with($feature->id(), $featureConnection); + + $featureConnection = $featureConnection + ->withCommands(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $feature)->vertices()) + ->withEvents(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $feature)->vertices()) + ->withAggregates(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_AGGREGATE, $feature)->vertices()) + ->withDocuments(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $feature)->vertices()) + ->withExternalSystems(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EXTERNAL_SYSTEM, $feature)->vertices()) + ->withHotSpots(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_HOT_SPOT, $feature)->vertices()) + ->withPolicies(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_POLICY, $feature)->vertices()) + ->withUis(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_UI, $feature)->vertices()); + + $this->featureConnectionMap = $this->featureConnectionMap->with($feature->id(), $featureConnection); + } + } + + return $this->featureConnectionMap; + } + public function boundedContextMap(): VertexMap { if (null === $this->boundedContextMap) { diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index 7e9cd54..e6fe03b 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -13,6 +13,7 @@ use EventEngine\InspectioGraph\AggregateType; use EventEngine\InspectioGraph\CommandType; use EventEngine\InspectioGraph\EventType; +use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraphCody\EventSourcingAnalyzer; use EventEngine\InspectioGraphCody\JsonNode; use PHPUnit\Framework\TestCase; @@ -52,7 +53,24 @@ public function it_returns_command_map_of_command_node(): void /** * @test */ - public function it_returns_aggregate_map_of_command_node(): void + public function it_returns_feature_connection_map_of_command_node(): void + { + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); + + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + + $this->assertCount(1, $featureConnectionMap); + $featureConnection = $featureConnectionMap->current(); + + $this->assertCommandAddBuilding($featureConnection->commandMap()->current()); + $this->assertAggregateBuilding($featureConnection->aggregateMap()->current(), 'buTwEKKNLBBo6WAERYN1Gn'); + } + + /** + * @test + */ + public function it_returns_aggregate_connection_map_of_command_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); @@ -61,7 +79,7 @@ public function it_returns_aggregate_map_of_command_node(): void $this->assertCount(1, $aggregateMap); $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'o67B4pXhbDvuF2BnBsBy27'); + $this->assertAggregateBuilding($aggregate->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); $commandMap = $aggregate->commandMap(); $this->assertCount(1, $commandMap); @@ -74,7 +92,7 @@ public function it_returns_aggregate_map_of_command_node(): void /** * @test */ - public function it_returns_aggregate_map_of_event_node(): void + public function it_returns_aggregate_connection_map_of_event_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); @@ -93,6 +111,31 @@ public function it_returns_aggregate_map_of_event_node(): void $this->assertCount(0, $commandMap); } + /** + * @test + */ + public function it_returns_feature_connection_map_of_event_node(): void + { + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); + + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + + $this->assertCount(1, $featureConnectionMap); + $featureConnection = $featureConnectionMap->current(); + + $aggregateMap = $featureConnection->aggregateMap(); + $this->assertCount(1, $aggregateMap); + $this->assertAggregateBuilding($aggregateMap->current(), 'buTwEKKNLBBo6WAERYN1Gn'); + + $eventMap = $featureConnection->eventMap(); + $this->assertCount(1, $eventMap); + $this->assertEventBuildingAdded($eventMap->current()); + + $commandMap = $featureConnection->commandMap(); + $this->assertCount(0, $commandMap); + } + /** * @test */ @@ -131,7 +174,7 @@ public function it_returns_event_map_of_aggregate_node(): void /** * @test */ - public function it_returns_aggregate_map_of_aggregate_node(): void + public function it_returns_aggregate_connection_map_of_aggregate_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); @@ -140,7 +183,7 @@ public function it_returns_aggregate_map_of_aggregate_node(): void $this->assertCount(1, $aggregateMap); $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'juqq2zqsYU44UgrAad65Ws'); + $this->assertAggregateBuilding($aggregate->aggregate(), 'eiaS8gtsBemMReTNbeNRXj'); $commandMap = $aggregate->commandMap(); $this->assertCount(1, $commandMap); @@ -165,45 +208,153 @@ public function it_returns_aggregate_map_of_aggregate_node(): void $this->assertCount(2, $events); } + /** + * @test + */ + public function it_returns_feature_connection_map_of_aggregate_node(): void + { + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); + + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + + $this->assertCount(1, $featureConnectionMap); + $featureConnection = $featureConnectionMap->current(); + + $commandMap = $featureConnection->commandMap(); + $this->assertCount(1, $commandMap); + $command = $commandMap->current(); + $this->assertCommandCheckInUser($command); + + $eventMap = $featureConnection->eventMap(); + $this->assertCount(2, $eventMap); + $event = $eventMap->current(); + $this->assertEventUserCheckedIn($event); + + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventDoubleCheckInDetected($event); + } + + /** + * @test + */ + public function it_returns_feature_connection_map(): void + { + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); + + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + + $this->assertCount(1, $featureConnectionMap); + $featureConnection = $featureConnectionMap->current(); + + $aggregateMap = $featureConnection->aggregateMap(); + $this->assertCount(1, $aggregateMap); + + $aggregate = $aggregateMap->current(); + $this->assertAggregateBuilding($aggregate, 'jKrpwfkdZnT5xMRKMYrgTF'); + $this->assertEquals($featureConnection->feature(), $featureConnectionMap->featureByAggregate($aggregate)); + + $this->assertFeatureCommandMap($featureConnection->commandMap()); + $this->assertFeatureEventMap($featureConnection->eventMap()); + $this->assertCount(2, $featureConnection->documentMap()); + + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + $this->assertCount(3, $aggregateConnectionMap); + + $aggregateConnection = $aggregateConnectionMap->current(); + $this->assertCommandAddBuilding($aggregateConnection->commandMap()->current()); + $this->assertEventBuildingAdded($aggregateConnection->eventMap()->current()); + + $aggregateConnectionMap->next(); + $aggregateConnection = $aggregateConnectionMap->current(); + $this->assertCommandCheckInUser($aggregateConnection->commandMap()->current()); + + $eventMap = $aggregateConnection->eventMap(); + $this->assertEventUserCheckedIn($eventMap->current()); + + $eventMap->next(); + $this->assertEventDoubleCheckInDetected($eventMap->current()); + } + + private function assertFeatureCommandMap(VertexMap $commandMap): void + { + $this->assertCount(3, $commandMap); + + $command = $commandMap->current(); + $this->assertCommandAddBuilding($command); + + $commandMap->next(); + $command = $commandMap->current(); + $this->assertCommandCheckInUser($command); + } + + private function assertFeatureEventMap(VertexMap $eventMap): void + { + $this->assertCount(4, $eventMap); + $event = $eventMap->current(); + $this->assertEventBuildingAdded($event); + + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventUserCheckedIn($event); + + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventUserCheckedOut($event); + + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventDoubleCheckInDetected($event); + } + private function assertAggregateBuilding(AggregateType $aggregate, string $id): void { $this->assertSame($id, $aggregate->id()); $this->assertSame('Building', $aggregate->name()); - $this->assertSame('Building', $aggregate->label()); + $this->assertSame('Building ', $aggregate->label()); } private function assertCommandAddBuilding(CommandType $command): void { - $this->assertSame('i5hHo93xQP8cvhdTh25DHq', $command->id()); + $this->assertSame('9bJ5Y7yuBcfWyei7i2ZSDC', $command->id()); $this->assertSame('Add Building', $command->name()); - $this->assertSame('Add Building', $command->label()); } private function assertEventBuildingAdded(EventType $event): void { - $this->assertSame('ctuHbHKF1pwqQfa3VZ1nfz', $event->id()); + $this->assertSame('tF2ZuZCXsdQMhRmRXydfuW', $event->id()); $this->assertSame('Building Added', $event->name()); $this->assertSame('Building Added', $event->label()); } private function assertCommandCheckInUser(CommandType $command): void { - $this->assertSame('i26gh6vK7QCsNwpvW4CuYX', $command->id()); - $this->assertSame('CheckInUser', $command->name()); - $this->assertSame('CheckInUser', $command->label()); + $this->assertSame('aKvhibi95v18MKjNjb6tL3', $command->id()); + $this->assertSame('Check In User', $command->name()); + $this->assertSame('Check In User', \trim($command->label())); } private function assertEventUserCheckedIn(EventType $event): void { - $this->assertSame('ondJmx9Xo19YLYJk1DqFeL', $event->id()); - $this->assertSame('UserCheckedIn', $event->name()); - $this->assertSame('UserCheckedIn', $event->label()); + $this->assertSame('q3thtbbiWsgyRqGadCBLte', $event->id()); + $this->assertSame('User Checked In', $event->name()); + $this->assertSame('User Checked In', $event->label()); + } + + private function assertEventUserCheckedOut(EventType $event): void + { + $this->assertSame('5cVD57Gt2HtxPU6zonD5vx', $event->id()); + $this->assertSame('User Checked Out', $event->name()); + $this->assertSame('User Checked Out', $event->label()); } private function assertEventDoubleCheckInDetected(EventType $event): void { - $this->assertSame('uNMg3RfYfAsdD7pvzt1zY9', $event->id()); - $this->assertSame('DoubleCheckInDetected', $event->name()); - $this->assertSame('DoubleCheckInDetected', $event->label()); + $this->assertSame('8H79vCoLa3Y2RrpVy7ZMYE', $event->id()); + $this->assertSame('Double Check In Detected', $event->name()); + $this->assertSame('Double Check In Detected ', $event->label()); } } diff --git a/tests/JsonNodeTest.php b/tests/JsonNodeTest.php index e64bb0f..c0a074a 100644 --- a/tests/JsonNodeTest.php +++ b/tests/JsonNodeTest.php @@ -25,8 +25,8 @@ public function it_can_be_created_from_json(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); - $this->assertSame('o67B4pXhbDvuF2BnBsBy27', $node->id()); - $this->assertSame('Building', $node->name()); + $this->assertSame('buTwEKKNLBBo6WAERYN1Gn', $node->id()); + $this->assertSame('Building', \trim($node->name())); $this->assertSame('aggregate', $node->type()); $this->assertFalse($node->isLayer()); $this->assertFalse($node->isDefaultLayer()); @@ -42,6 +42,9 @@ public function it_returns_parent_node(): void $parent = $node->parent(); $this->assertFeatureNode($parent); + $parent = $parent->parent(); + $this->assertBoundedContextNode($parent); + $parent = $parent->parent(); $this->assertBoardLayerNode($parent); } @@ -54,13 +57,16 @@ public function it_returns_sources(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); foreach ($node->sources() as $source) { - $this->assertSame('i5hHo93xQP8cvhdTh25DHq', $source->id()); - $this->assertSame('Add Building', $source->name()); + $this->assertSame('9bJ5Y7yuBcfWyei7i2ZSDC', $source->id()); + $this->assertSame('Add Building', \trim($source->name())); $this->assertSame('command', $source->type()); $this->assertFalse($source->isLayer()); $this->assertFalse($source->isDefaultLayer()); $parent = $node->parent(); + $this->assertFeatureNode($parent); + + $parent = $parent->parent(); $this->assertBoundedContextNode($parent); $parent = $parent->parent(); @@ -76,13 +82,16 @@ public function it_returns_targets(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); foreach ($node->targets() as $target) { - $this->assertSame('ctuHbHKF1pwqQfa3VZ1nfz', $target->id()); - $this->assertSame('BuildingAdded', $target->name()); + $this->assertSame('tF2ZuZCXsdQMhRmRXydfuW', $target->id()); + $this->assertSame('Building Added', $target->name()); $this->assertSame('event', $target->type()); $this->assertFalse($target->isLayer()); $this->assertFalse($target->isDefaultLayer()); $parent = $node->parent(); + $this->assertFeatureNode($parent); + + $parent = $parent->parent(); $this->assertBoundedContextNode($parent); $parent = $parent->parent(); @@ -103,8 +112,8 @@ private function assertFeatureNode(Node $node): void private function assertBoundedContextNode(Node $node): void { $this->assertInstanceOf(Node::class, $node); - $this->assertSame('stW5qRRPsQbowcqux7M2QX', $node->id()); - $this->assertSame('Building', $node->name()); + $this->assertSame('2FqsFfW5xGzooq2fdFTfaa', $node->id()); + $this->assertSame('Hotel', $node->name()); $this->assertSame('boundedContext', $node->type()); $this->assertFalse($node->isLayer()); $this->assertFalse($node->isDefaultLayer()); diff --git a/tests/_files/add_building.json b/tests/_files/add_building.json index a9e2d86..5b0f98c 100644 --- a/tests/_files/add_building.json +++ b/tests/_files/add_building.json @@ -1,7 +1,7 @@ { "node": { - "id": "i5hHo93xQP8cvhdTh25DHq", - "name": "Add Building", + "id": "9bJ5Y7yuBcfWyei7i2ZSDC", + "name": "Add Building\n", "type": "command", "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=9bJ5Y7yuBcfWyei7i2ZSDC&clicks=1", "tags": [], @@ -16,20 +16,37 @@ "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", - "link": null, + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -37,8 +54,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -656, - "y": -320 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -46,8 +63,8 @@ "sourcesList": [], "targetsList": [ { - "id": "o67B4pXhbDvuF2BnBsBy27", - "name": "Building", + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", "type": "aggregate", "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", "tags": [], @@ -62,20 +79,37 @@ "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", - "link": null, + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -83,8 +117,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -656, - "y": -320 + "x": 280.5, + "y": 249 }, "metadata": null }, diff --git a/tests/_files/building.json b/tests/_files/building.json index bf4893a..987707f 100644 --- a/tests/_files/building.json +++ b/tests/_files/building.json @@ -1,32 +1,52 @@ { "node": { - "id": "o67B4pXhbDvuF2BnBsBy27", - "name": "Building", + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -34,41 +54,61 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 280.5, + "y": 249 }, "metadata": null }, "childrenList": [], "sourcesList": [ { - "id": "i5hHo93xQP8cvhdTh25DHq", - "name": "Add Building", + "id": "9bJ5Y7yuBcfWyei7i2ZSDC", + "name": "Add Building\n", "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=9bJ5Y7yuBcfWyei7i2ZSDC&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -76,8 +116,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -85,41 +125,61 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": 264, - "y": 224 + "x": 464, + "y": 319 }, - "metadata": "{\n \"newAggregate\": true,\n \"schema\": {\n \"type\": \"object\",\n \"required\": [\n \"buildingId\",\n \"name\"\n ],\n \"additionalProperties\": false,\n \"properties\": {\n \"buildingId\": {\n \"type\": \"string\",\n \"pattern\": \"^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$\"\n },\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 2\n }\n }\n }\n}" + "metadata": "{\n \"newAggregate\": true,\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n } \n}\n" } ], "targetsList": [ { - "id": "ctuHbHKF1pwqQfa3VZ1nfz", - "name": "BuildingAdded", + "id": "tF2ZuZCXsdQMhRmRXydfuW", + "name": "Building Added", "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tF2ZuZCXsdQMhRmRXydfuW&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -127,8 +187,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -136,16 +196,16 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": 728, - "y": 224 + "x": 463, + "y": 768 }, - "metadata": null + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" } ], "geometry": { - "x": 504, - "y": 224 + "x": 464, + "y": 544 }, - "metadata": null + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" } } diff --git a/tests/_files/building_added.json b/tests/_files/building_added.json index ec0f140..0249b25 100644 --- a/tests/_files/building_added.json +++ b/tests/_files/building_added.json @@ -1,6 +1,6 @@ { "node": { - "id": "ctuHbHKF1pwqQfa3VZ1nfz", + "id": "tF2ZuZCXsdQMhRmRXydfuW", "name": "Building Added", "type": "event", "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tF2ZuZCXsdQMhRmRXydfuW&clicks=1", @@ -16,20 +16,37 @@ "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", - "link": null, + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -37,8 +54,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -656, - "y": -320 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -46,7 +63,7 @@ "sourcesList": [ { "id": "buTwEKKNLBBo6WAERYN1Gn", - "name": "Building", + "name": "Building ", "type": "aggregate", "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", "tags": [], @@ -61,20 +78,37 @@ "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", - "link": null, + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -82,8 +116,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -656, - "y": -320 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -100,7 +134,7 @@ "targetsList": [ { "id": "4gYkBjXufnkWMN5ybfBvPq", - "name": "Building", + "name": "Building ", "type": "document", "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=4gYkBjXufnkWMN5ybfBvPq&clicks=1", "tags": [], @@ -115,20 +149,37 @@ "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", - "link": null, + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -136,8 +187,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -656, - "y": -320 + "x": 280.5, + "y": 249 }, "metadata": null }, @@ -148,7 +199,7 @@ "x": 816, "y": 544 }, - "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n} \n" + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" } ], "geometry": { diff --git a/tests/_files/building_user.json b/tests/_files/building_user.json index 9e1c304..8f80b96 100644 --- a/tests/_files/building_user.json +++ b/tests/_files/building_user.json @@ -1,32 +1,52 @@ { "node": { - "id": "juqq2zqsYU44UgrAad65Ws", - "name": "Building", + "id": "eiaS8gtsBemMReTNbeNRXj", + "name": "Building ", "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=eiaS8gtsBemMReTNbeNRXj&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -34,41 +54,61 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 49, + "y": 201 }, "metadata": null }, "childrenList": [], "sourcesList": [ { - "id": "i26gh6vK7QCsNwpvW4CuYX", - "name": "CheckInUser", + "id": "aKvhibi95v18MKjNjb6tL3", + "name": "Check In User\n", "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=aKvhibi95v18MKjNjb6tL3&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -76,8 +116,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 49, + "y": 201 }, "metadata": null }, @@ -85,41 +125,61 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": 256, - "y": 592 + "x": 736, + "y": 198.5 }, - "metadata": "{\n \"newAggregate\": false,\n \"schema\": {\n \"type\": \"object\",\n \"required\": [\n \"buildingId\",\n \"name\"\n ],\n \"additionalProperties\": false,\n \"properties\": {\n \"buildingId\": {\n \"type\": \"string\",\n \"pattern\": \"^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$\"\n },\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 2\n }\n }\n }\n}" + "metadata": null } ], "targetsList": [ { - "id": "ondJmx9Xo19YLYJk1DqFeL", - "name": "UserCheckedIn", + "id": "q3thtbbiWsgyRqGadCBLte", + "name": "User Checked In", "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=q3thtbbiWsgyRqGadCBLte&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -127,8 +187,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 49, + "y": 201 }, "metadata": null }, @@ -136,39 +196,59 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": 728, - "y": 480 + "x": 847, + "y": 617.5 }, "metadata": null }, { - "id": "uNMg3RfYfAsdD7pvzt1zY9", - "name": "DoubleCheckInDetected", + "id": "8H79vCoLa3Y2RrpVy7ZMYE", + "name": "Double Check In Detected ", "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=8H79vCoLa3Y2RrpVy7ZMYE&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { "id": "stW5qRRPsQbowcqux7M2QX", "name": "Building", - "type": "boundedContext", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", "tags": [], "layer": false, "defaultLayer": false, "parent": { - "id": "7fe80d19-d317-4e9d-8296-96c598786d78", - "name": "Board", - "type": "layer", + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", "tags": [], - "layer": true, - "defaultLayer": true, - "parent": null, + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, "childrenList": [], "sourcesList": [], "targetsList": [], "geometry": { - "x": 0, - "y": 0 + "x": -993, + "y": -201 }, "metadata": null }, @@ -176,8 +256,8 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": -688, - "y": -416 + "x": 49, + "y": 201 }, "metadata": null }, @@ -185,16 +265,16 @@ "sourcesList": [], "targetsList": [], "geometry": { - "x": 728, - "y": 704 + "x": 640, + "y": 618 }, "metadata": null } ], "geometry": { - "x": 496, - "y": 592 + "x": 736, + "y": 409.5 }, - "metadata": null + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" } } diff --git a/tests/_files/feature_building.json b/tests/_files/feature_building.json new file mode 100644 index 0000000..01ac7c1 --- /dev/null +++ b/tests/_files/feature_building.json @@ -0,0 +1,3459 @@ +{ + "node": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [ + { + "id": "9bJ5Y7yuBcfWyei7i2ZSDC", + "name": "Add Building\n", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=9bJ5Y7yuBcfWyei7i2ZSDC&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [ + { + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 80, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "geometry": { + "x": 80, + "y": 184.5 + }, + "metadata": "{\n \"newAggregate\": true,\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n } \n}\n" + }, + { + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "9bJ5Y7yuBcfWyei7i2ZSDC", + "name": "Add Building\n", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=9bJ5Y7yuBcfWyei7i2ZSDC&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 80, + "y": 184.5 + }, + "metadata": "{\n \"newAggregate\": true,\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n } \n}\n" + } + ], + "targetsList": [ + { + "id": "tF2ZuZCXsdQMhRmRXydfuW", + "name": "Building Added", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tF2ZuZCXsdQMhRmRXydfuW&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 79, + "y": 633.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "geometry": { + "x": 80, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + }, + { + "id": "mtUykv9KSfvQEFbZV2PNk3", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=mtUykv9KSfvQEFbZV2PNk3&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "tF2ZuZCXsdQMhRmRXydfuW", + "name": "Building Added", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tF2ZuZCXsdQMhRmRXydfuW&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 80, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "targetsList": [ + { + "id": "4gYkBjXufnkWMN5ybfBvPq", + "name": "Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=4gYkBjXufnkWMN5ybfBvPq&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 432, + "y": 409.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" + } + ], + "geometry": { + "x": 79, + "y": 633.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + }, + { + "id": "vW98moff8Nj9iHZgSY3pDw", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=vW98moff8Nj9iHZgSY3pDw&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "4gYkBjXufnkWMN5ybfBvPq", + "name": "Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=4gYkBjXufnkWMN5ybfBvPq&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "tF2ZuZCXsdQMhRmRXydfuW", + "name": "Building Added", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tF2ZuZCXsdQMhRmRXydfuW&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 79, + "y": 633.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "targetsList": [ + { + "id": "aKvhibi95v18MKjNjb6tL3", + "name": "Check In User\n", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=aKvhibi95v18MKjNjb6tL3&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 736, + "y": 198.5 + }, + "metadata": null + } + ], + "geometry": { + "x": 432, + "y": 409.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" + }, + { + "id": "kxMZffLJbbP48EnjX9hDjh", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=kxMZffLJbbP48EnjX9hDjh&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "aKvhibi95v18MKjNjb6tL3", + "name": "Check In User\n", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=aKvhibi95v18MKjNjb6tL3&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "4gYkBjXufnkWMN5ybfBvPq", + "name": "Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=4gYkBjXufnkWMN5ybfBvPq&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 432, + "y": 409.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" + } + ], + "targetsList": [ + { + "id": "eiaS8gtsBemMReTNbeNRXj", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=eiaS8gtsBemMReTNbeNRXj&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 736, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "geometry": { + "x": 736, + "y": 198.5 + }, + "metadata": null + }, + { + "id": "8S8yYUBXEN9nEX4QUvqxNJ", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=8S8yYUBXEN9nEX4QUvqxNJ&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "6LnFVPkNPVDkK8KTJnLBAi", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=6LnFVPkNPVDkK8KTJnLBAi&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "eiaS8gtsBemMReTNbeNRXj", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=eiaS8gtsBemMReTNbeNRXj&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "aKvhibi95v18MKjNjb6tL3", + "name": "Check In User\n", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=aKvhibi95v18MKjNjb6tL3&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 736, + "y": 198.5 + }, + "metadata": null + } + ], + "targetsList": [ + { + "id": "q3thtbbiWsgyRqGadCBLte", + "name": "User Checked In", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=q3thtbbiWsgyRqGadCBLte&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 847, + "y": 617.5 + }, + "metadata": null + }, + { + "id": "8H79vCoLa3Y2RrpVy7ZMYE", + "name": "Double Check In Detected ", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=8H79vCoLa3Y2RrpVy7ZMYE&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 640, + "y": 618 + }, + "metadata": null + } + ], + "geometry": { + "x": 736, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + }, + { + "id": "cWw9Ebuhn8psgpzAr9Tyi7", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=cWw9Ebuhn8psgpzAr9Tyi7&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "kLpw5wkQHPxMUL9j4imCTB", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=kLpw5wkQHPxMUL9j4imCTB&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "q3thtbbiWsgyRqGadCBLte", + "name": "User Checked In", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=q3thtbbiWsgyRqGadCBLte&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "eiaS8gtsBemMReTNbeNRXj", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=eiaS8gtsBemMReTNbeNRXj&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 736, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "targetsList": [ + { + "id": "t4mMTjg462VRvMW1L6nSGB", + "name": "Users In Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=t4mMTjg462VRvMW1L6nSGB&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1040, + "y": 410 + }, + "metadata": null + } + ], + "geometry": { + "x": 847, + "y": 617.5 + }, + "metadata": null + }, + { + "id": "etdC2m78oWsKbYFYGxkXZn", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=etdC2m78oWsKbYFYGxkXZn&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "8L23QRzy31wpkKhsqqTCHZ", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=8L23QRzy31wpkKhsqqTCHZ&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "t4mMTjg462VRvMW1L6nSGB", + "name": "Users In Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=t4mMTjg462VRvMW1L6nSGB&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "q3thtbbiWsgyRqGadCBLte", + "name": "User Checked In", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=q3thtbbiWsgyRqGadCBLte&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 847, + "y": 617.5 + }, + "metadata": null + } + ], + "targetsList": [ + { + "id": "dkNTGinM6VY1Qu8zyGURU1", + "name": "Check Out User", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=dkNTGinM6VY1Qu8zyGURU1&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296, + "y": 210.99999999999997 + }, + "metadata": null + } + ], + "geometry": { + "x": 1040, + "y": 410 + }, + "metadata": null + }, + { + "id": "tqXzpKnBmB1rLpr8R2zqJe", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=tqXzpKnBmB1rLpr8R2zqJe&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "dkNTGinM6VY1Qu8zyGURU1", + "name": "Check Out User", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=dkNTGinM6VY1Qu8zyGURU1&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "t4mMTjg462VRvMW1L6nSGB", + "name": "Users In Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=t4mMTjg462VRvMW1L6nSGB&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1040, + "y": 410 + }, + "metadata": null + } + ], + "targetsList": [ + { + "id": "jKrpwfkdZnT5xMRKMYrgTF", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=jKrpwfkdZnT5xMRKMYrgTF&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296, + "y": 410 + }, + "metadata": null + } + ], + "geometry": { + "x": 1296, + "y": 210.99999999999997 + }, + "metadata": null + }, + { + "id": "9DKYrUaQhvFkwZ4DutK5gZ", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=9DKYrUaQhvFkwZ4DutK5gZ&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "jKrpwfkdZnT5xMRKMYrgTF", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=jKrpwfkdZnT5xMRKMYrgTF&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "dkNTGinM6VY1Qu8zyGURU1", + "name": "Check Out User", + "type": "command", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=dkNTGinM6VY1Qu8zyGURU1&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296, + "y": 210.99999999999997 + }, + "metadata": null + } + ], + "targetsList": [ + { + "id": "5cVD57Gt2HtxPU6zonD5vx", + "name": "User Checked Out", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=5cVD57Gt2HtxPU6zonD5vx&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296.0000000000002, + "y": 618 + }, + "metadata": null + } + ], + "geometry": { + "x": 1296, + "y": 410 + }, + "metadata": null + }, + { + "id": "etP7ZtNMrpQ6eeVByghvo6", + "name": "", + "type": "edge", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=etP7ZtNMrpQ6eeVByghvo6&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + { + "id": "5cVD57Gt2HtxPU6zonD5vx", + "name": "User Checked Out", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=5cVD57Gt2HtxPU6zonD5vx&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "jKrpwfkdZnT5xMRKMYrgTF", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=jKrpwfkdZnT5xMRKMYrgTF&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296, + "y": 410 + }, + "metadata": null + } + ], + "targetsList": [ + { + "id": "f9iDWVAz8qT4j37oNzoU1p", + "name": "Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=f9iDWVAz8qT4j37oNzoU1p&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1568, + "y": 410 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" + } + ], + "geometry": { + "x": 1296.0000000000002, + "y": 618 + }, + "metadata": null + }, + { + "id": "f9iDWVAz8qT4j37oNzoU1p", + "name": "Building ", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=f9iDWVAz8qT4j37oNzoU1p&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "5cVD57Gt2HtxPU6zonD5vx", + "name": "User Checked Out", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=5cVD57Gt2HtxPU6zonD5vx&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 1296.0000000000002, + "y": 618 + }, + "metadata": null + } + ], + "targetsList": [], + "geometry": { + "x": 1568, + "y": 410 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"BuildingId\",\n \"name\": \"Name|ns:Building\"\n }\n} \n" + }, + { + "id": "8H79vCoLa3Y2RrpVy7ZMYE", + "name": "Double Check In Detected ", + "type": "event", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=8H79vCoLa3Y2RrpVy7ZMYE&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [ + { + "id": "eiaS8gtsBemMReTNbeNRXj", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=eiaS8gtsBemMReTNbeNRXj&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -993, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 736, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "targetsList": [], + "geometry": { + "x": 640, + "y": 618 + }, + "metadata": null + } + ], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 49, + "y": 201 + }, + "metadata": null + } +} From e9b8b64a533c47a25a4253a2e6316538395c5520 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Mon, 19 Apr 2021 10:33:05 +0200 Subject: [PATCH 03/18] Add support for connected documents for aggregate --- src/EventSourcingAnalyzer.php | 7 ++ tests/EventSourcingAnalyzerTest.php | 33 +++++++ tests/_files/name_vo.json | 141 ++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 tests/_files/name_vo.json diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index 5f2d1dd..b3e6c0b 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -183,6 +183,7 @@ private function filterVertexTypeWithConnectionOf(string $vertexType, VertexType break; case VertexType::TYPE_COMMAND: case VertexType::TYPE_EVENT: + case VertexType::TYPE_DOCUMENT: foreach ($this->node->sources() as $source) { if ($this->node->type() === $vertexType && $this->areNodesEqual($source, $node) @@ -304,6 +305,7 @@ public function aggregateConnectionMap(): AggregateConnectionMap $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); $commandVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $aggregate); $eventVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $aggregate); + $documentVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $aggregate); $countCommandVertices = \count($commandVertices); @@ -321,6 +323,11 @@ public function aggregateConnectionMap(): AggregateConnectionMap // @phpstan-ignore-next-line $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); } + + if (\count($documentVertices) > 0) { + $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); + } + $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); } } diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index e6fe03b..9a29d84 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -12,6 +12,7 @@ use EventEngine\InspectioGraph\AggregateType; use EventEngine\InspectioGraph\CommandType; +use EventEngine\InspectioGraph\DocumentType; use EventEngine\InspectioGraph\EventType; use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraphCody\EventSourcingAnalyzer; @@ -111,6 +112,31 @@ public function it_returns_aggregate_connection_map_of_event_node(): void $this->assertCount(0, $commandMap); } + /** + * @test + */ + public function it_returns_aggregate_connection_map_of_document_node(): void + { + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'name_vo.json')); + + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + + $this->assertCount(1, $aggregateMap); + $aggregate = $aggregateMap->current(); + $this->assertAggregateBuilding($aggregate->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); + + $documentMap = $aggregate->documentMap(); + $this->assertCount(1, $documentMap); + $this->assertDocumentName($documentMap->current()); + + $commandMap = $aggregate->commandMap(); + $this->assertCount(0, $commandMap); + + $eventMap = $aggregate->eventMap(); + $this->assertCount(0, $eventMap); + } + /** * @test */ @@ -357,4 +383,11 @@ private function assertEventDoubleCheckInDetected(EventType $event): void $this->assertSame('Double Check In Detected', $event->name()); $this->assertSame('Double Check In Detected ', $event->label()); } + + private function assertDocumentName(DocumentType $event): void + { + $this->assertSame('a4HLmzMb2g2MVQWXy4BKN1', $event->id()); + $this->assertSame('Name', $event->name()); + $this->assertSame('Name', $event->label()); + } } diff --git a/tests/_files/name_vo.json b/tests/_files/name_vo.json new file mode 100644 index 0000000..2b54d7c --- /dev/null +++ b/tests/_files/name_vo.json @@ -0,0 +1,141 @@ +{ + "node": { + "id": "a4HLmzMb2g2MVQWXy4BKN1", + "name": "Name", + "type": "document", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=a4HLmzMb2g2MVQWXy4BKN1&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -1680, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 288, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [ + { + "id": "buTwEKKNLBBo6WAERYN1Gn", + "name": "Building ", + "type": "aggregate", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=buTwEKKNLBBo6WAERYN1Gn&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "stW5qRRPsQbowcqux7M2QX", + "name": "Building", + "type": "feature", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=stW5qRRPsQbowcqux7M2QX&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "2FqsFfW5xGzooq2fdFTfaa", + "name": "Hotel", + "type": "boundedContext", + "link": "https://inspect.event-engine.io/inspectio/board/3fc5479e-cf3c-459a-96fd-5b62d002f843?cells=2FqsFfW5xGzooq2fdFTfaa&clicks=1", + "tags": [], + "layer": false, + "defaultLayer": false, + "parent": { + "id": "7fe80d19-d317-4e9d-8296-96c598786d78", + "name": "Board", + "type": "layer", + "link": null, + "tags": [], + "layer": true, + "defaultLayer": true, + "parent": null, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 0, + "y": 0 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": -1680, + "y": -201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 288, + "y": 201 + }, + "metadata": null + }, + "childrenList": [], + "sourcesList": [], + "targetsList": [], + "geometry": { + "x": 528, + "y": 409.5 + }, + "metadata": "{\n \"identifier\": \"buildingId\",\n \"shorthand\": true,\n \"schema\": {\n \"buildingId\": \"string|format:uuid\",\n \"name\": \"string\"\n }\n}" + } + ], + "geometry": { + "x": 224, + "y": 419.5 + }, + "metadata": "{\n \"shorthand\": true,\n \"schema\": \"string|ns:Building\"\n} \n" + } +} From 57c6fc185f951d9a5b7298d30db2123c639de6be Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 28 May 2021 15:23:12 +0200 Subject: [PATCH 04/18] Add support for analyzing more than one node to build up all connections --- src/EventSourcingAnalyzer.php | 165 +++++++++++----- tests/EventSourcingAnalyzerTest.php | 287 ++++++++++++++++++++++++++-- 2 files changed, 392 insertions(+), 60 deletions(-) diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index b3e6c0b..a5e6e2f 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -106,6 +106,53 @@ public function __construct( $this->node = $node; $this->filterName = $filterName; $this->metadataFactory = $metadataFactory; + + $this->analyse($node); // for BC, $node should be removed later + } + + public function analyse(Node $node): void + { + $this->node = $node; + + // all maps can be analyzed in parallel + $this->commandMap = $this->commandMap()->with(...$this->vertexMapByType(VertexType::TYPE_COMMAND)); + $this->aggregateMap = $this->aggregateMap()->with(...$this->vertexMapByType(VertexType::TYPE_AGGREGATE)); + $this->eventMap = $this->eventMap()->with(...$this->vertexMapByType(VertexType::TYPE_EVENT)); + $this->documentMap = $this->documentMap()->with(...$this->vertexMapByType(VertexType::TYPE_DOCUMENT)); + $this->policyMap = $this->policyMap()->with(...$this->vertexMapByType(VertexType::TYPE_POLICY)); + $this->uiMap = $this->uiMap()->with(...$this->vertexMapByType(VertexType::TYPE_UI)); + $this->externalSystemMap = $this->externalSystemMap()->with(...$this->vertexMapByType(VertexType::TYPE_EXTERNAL_SYSTEM)); + $this->hotSpotMap = $this->hotSpotMap()->with(...$this->vertexMapByType(VertexType::TYPE_HOT_SPOT)); + $this->featureMap = $this->featureMap()->with(...$this->vertexMapByType(VertexType::TYPE_FEATURE)); + $this->boundedContextMap = $this->boundedContextMap()->with(...$this->boundedContextMap()->vertices()); + + // all connection maps can be analyzed in parallel + foreach ($this->determineAggregateConnection() as $aggregateConnection) { + $this->aggregateConnectionMap = $this->aggregateConnectionMap()->with( + $aggregateConnection->aggregate()->id(), + $aggregateConnection + ); + } + + foreach ($this->determineFeatureConnectionMap() as $featureConnection) { + $this->featureConnectionMap = $this->featureConnectionMap()->with( + $featureConnection->feature()->id(), + $featureConnection + ); + } + + $this->commandMap()->rewind(); + $this->aggregateMap()->rewind(); + $this->eventMap()->rewind(); + $this->aggregateConnectionMap()->rewind(); + $this->documentMap()->rewind(); + $this->policyMap()->rewind(); + $this->uiMap()->rewind(); + $this->externalSystemMap()->rewind(); + $this->hotSpotMap()->rewind(); + $this->featureMap()->rewind(); + $this->featureConnectionMap()->rewind(); + $this->boundedContextMap()->rewind(); } /** @@ -295,44 +342,58 @@ public function aggregateConnectionMap(): AggregateConnectionMap if (null === $this->aggregateConnectionMap) { $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - foreach ($this->filterVerticesByType(VertexType::TYPE_AGGREGATE) as $node) { - $aggregate = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - $name = $aggregate->name(); + foreach ($this->determineAggregateConnection() as $aggregateConnection) { + $this->aggregateConnectionMap = $this->aggregateConnectionMap->with( + $aggregateConnection->aggregate()->id(), + $aggregateConnection + ); + } + } - // @phpstan-ignore-next-line - $aggregateConnection = new AggregateConnection($aggregate); + return $this->aggregateConnectionMap; + } - $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); - $commandVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $aggregate); - $eventVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $aggregate); - $documentVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $aggregate); + private function determineAggregateConnection(): AggregateConnectionMap + { + $aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - $countCommandVertices = \count($commandVertices); + foreach ($this->filterVerticesByType(VertexType::TYPE_AGGREGATE) as $node) { + $aggregate = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); + $name = $aggregate->name(); - if ($countCommandVertices > 1) { - throw new RuntimeException( - \sprintf('Multiple command connections to aggregate "%s" found. Can not handle it.', $name) - ); - } + // @phpstan-ignore-next-line + $aggregateConnection = new AggregateConnection($aggregate); - if ($countCommandVertices === 1) { - $command = $commandVertices->current(); - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); - } elseif (\count($eventVertices) > 0) { - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); - } + $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); + $commandVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $aggregate); + $eventVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $aggregate); + $documentVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $aggregate); - if (\count($documentVertices) > 0) { - $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); - } + $countCommandVertices = \count($commandVertices); + + if ($countCommandVertices > 1) { + throw new RuntimeException( + \sprintf('Multiple command connections to aggregate "%s" found. Can not handle it.', $name) + ); + } - $this->aggregateConnectionMap = $this->aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); + if ($countCommandVertices === 1) { + $command = $commandVertices->current(); + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); + } elseif (\count($eventVertices) > 0) { + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); } + + if (\count($documentVertices) > 0) { + $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); + } + + $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); } - return $this->aggregateConnectionMap; + return $aggregateConnectionMap; } public function documentMap(): VertexMap @@ -466,29 +527,43 @@ public function featureConnectionMap(): FeatureConnectionMap if (null === $this->featureConnectionMap) { $this->featureConnectionMap = FeatureConnectionMap::emptyMap(); - foreach ($this->filterVerticesByType(VertexType::TYPE_FEATURE) as $node) { - $feature = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); + foreach ($this->determineFeatureConnectionMap() as $featureConnection) { + $this->featureConnectionMap = $this->featureConnectionMap->with( + $featureConnection->feature()->id(), + $featureConnection + ); + } + } - // @phpstan-ignore-next-line - $featureConnection = new FeatureConnection($feature); + return $this->featureConnectionMap; + } + + private function determineFeatureConnectionMap(): FeatureConnectionMap + { + $featureConnectionMap = FeatureConnectionMap::emptyMap(); - $this->featureConnectionMap = $this->featureConnectionMap->with($feature->id(), $featureConnection); + foreach ($this->filterVerticesByType(VertexType::TYPE_FEATURE) as $node) { + $feature = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - $featureConnection = $featureConnection - ->withCommands(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $feature)->vertices()) - ->withEvents(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $feature)->vertices()) - ->withAggregates(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_AGGREGATE, $feature)->vertices()) - ->withDocuments(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $feature)->vertices()) - ->withExternalSystems(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EXTERNAL_SYSTEM, $feature)->vertices()) - ->withHotSpots(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_HOT_SPOT, $feature)->vertices()) - ->withPolicies(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_POLICY, $feature)->vertices()) - ->withUis(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_UI, $feature)->vertices()); + // @phpstan-ignore-next-line + $featureConnection = new FeatureConnection($feature); - $this->featureConnectionMap = $this->featureConnectionMap->with($feature->id(), $featureConnection); - } + $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); + + $featureConnection = $featureConnection + ->withCommands(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $feature)->vertices()) + ->withEvents(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $feature)->vertices()) + ->withAggregates(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_AGGREGATE, $feature)->vertices()) + ->withDocuments(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $feature)->vertices()) + ->withExternalSystems(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EXTERNAL_SYSTEM, $feature)->vertices()) + ->withHotSpots(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_HOT_SPOT, $feature)->vertices()) + ->withPolicies(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_POLICY, $feature)->vertices()) + ->withUis(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_UI, $feature)->vertices()); + + $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); } - return $this->featureConnectionMap; + return $featureConnectionMap; } public function boundedContextMap(): VertexMap diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index 9a29d84..e8aa2a9 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -12,6 +12,7 @@ use EventEngine\InspectioGraph\AggregateType; use EventEngine\InspectioGraph\CommandType; +use EventEngine\InspectioGraph\Connection\AggregateConnection; use EventEngine\InspectioGraph\DocumentType; use EventEngine\InspectioGraph\EventType; use EventEngine\InspectioGraph\VertexMap; @@ -48,7 +49,7 @@ public function it_returns_command_map_of_command_node(): void $this->assertCount(1, $commandMap); $command = $commandMap->current(); - $this->assertCommandAddBuilding($command); + $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); } /** @@ -64,7 +65,7 @@ public function it_returns_feature_connection_map_of_command_node(): void $this->assertCount(1, $featureConnectionMap); $featureConnection = $featureConnectionMap->current(); - $this->assertCommandAddBuilding($featureConnection->commandMap()->current()); + $this->assertCommandAddBuilding($featureConnection->commandMap()->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); $this->assertAggregateBuilding($featureConnection->aggregateMap()->current(), 'buTwEKKNLBBo6WAERYN1Gn'); } @@ -76,18 +77,62 @@ public function it_returns_aggregate_connection_map_of_command_node(): void $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); - $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - $this->assertCount(1, $aggregateMap); - $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); + $this->assertCount(1, $aggregateConnectionMap); - $commandMap = $aggregate->commandMap(); + $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnectionMap->current()); + } + + private function assertAggregateConnectionMapOfCommandAddBuilding( + AggregateConnection $aggregateConnection, + bool $withEvent = false): void + { + $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); + + $commandMap = $aggregateConnection->commandMap(); $this->assertCount(1, $commandMap); - $this->assertCommandAddBuilding($commandMap->current()); + $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - $eventMap = $aggregate->eventMap(); - $this->assertCount(0, $eventMap); + $eventMap = $aggregateConnection->eventMap(); + + if ($withEvent === false) { + $this->assertCount(0, $eventMap); + } else { + $this->assertCount(1, $eventMap); + $this->assertEventBuildingAdded($eventMap->current()); + } + } + + private function assertAggregateConnectionMapOfCommandCheckInUser(AggregateConnection $aggregateConnection): void + { + $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'eiaS8gtsBemMReTNbeNRXj'); + + $commandMap = $aggregateConnection->commandMap(); + $this->assertCount(1, $commandMap); + $this->assertCommandCheckInUser($commandMap->current()); + + $eventMap = $aggregateConnection->eventMap(); + $eventMap->rewind(); + $this->assertCount(2, $eventMap); + $this->assertEventUserCheckedIn($eventMap->current()); + + $eventMap->next(); + $this->assertEventDoubleCheckInDetected($eventMap->current()); + } + + private function assertAggregateConnectionMapOfCommandCheckOutUser(AggregateConnection $aggregateConnection): void + { + $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'jKrpwfkdZnT5xMRKMYrgTF'); + + $commandMap = $aggregateConnection->commandMap(); + $this->assertCount(1, $commandMap); + $this->assertCommandCheckOutUser($commandMap->current()); + + $eventMap = $aggregateConnection->eventMap(); + $eventMap->rewind(); + $this->assertCount(1, $eventMap); + $this->assertEventUserCheckedOut($eventMap->current()); } /** @@ -175,7 +220,7 @@ public function it_returns_command_map_of_aggregate_node(): void $this->assertCount(1, $commandMap); $command = $commandMap->current(); - $this->assertCommandAddBuilding($command); + $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); } /** @@ -291,7 +336,7 @@ public function it_returns_feature_connection_map(): void $this->assertCount(3, $aggregateConnectionMap); $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertCommandAddBuilding($aggregateConnection->commandMap()->current()); + $this->assertCommandAddBuilding($aggregateConnection->commandMap()->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); $this->assertEventBuildingAdded($aggregateConnection->eventMap()->current()); $aggregateConnectionMap->next(); @@ -305,12 +350,203 @@ public function it_returns_feature_connection_map(): void $this->assertEventDoubleCheckInDetected($eventMap->current()); } + /** + * @test + */ + public function it_analysis_nodes(): void + { + // order is important for assertions + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); + $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + + $this->assertCount(1, $eventSourcingAnalyzer->commandMap()); + $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); + $this->assertCount(0, $eventSourcingAnalyzer->eventMap()); + $this->assertCount(1, $eventSourcingAnalyzer->aggregateConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->documentMap()); + $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); + $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); + $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); + $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); + $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', false); + + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); + $eventSourcingAnalyzer->analyse($node); + + $this->assertCount(2, $eventSourcingAnalyzer->commandMap()); + $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); + $this->assertCount(2, $eventSourcingAnalyzer->eventMap()); + $this->assertCount(2, $eventSourcingAnalyzer->aggregateConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->documentMap()); + $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); + $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); + $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); + $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); + $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'eiaS8gtsBemMReTNbeNRXj', false); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); + $eventSourcingAnalyzer->analyse($node); + $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); + + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); + $eventSourcingAnalyzer->analyse($node); + + $this->assertCount(2, $eventSourcingAnalyzer->commandMap()); + $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); + $this->assertCount(3, $eventSourcingAnalyzer->eventMap()); + $this->assertCount(2, $eventSourcingAnalyzer->aggregateConnectionMap()); + $this->assertCount(1, $eventSourcingAnalyzer->documentMap()); + $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); + $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); + $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); + $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); + $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); + + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); + $eventSourcingAnalyzer->analyse($node); + + $this->assertCount(3, $eventSourcingAnalyzer->commandMap()); + $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); + $this->assertCount(4, $eventSourcingAnalyzer->eventMap()); + $this->assertCount(3, $eventSourcingAnalyzer->aggregateConnectionMap()); + $this->assertCount(2, $eventSourcingAnalyzer->documentMap()); + $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); + $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); + $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); + $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); + $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); + $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); + $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'jKrpwfkdZnT5xMRKMYrgTF', true); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuilding($eventSourcingAnalyzer, 'f9iDWVAz8qT4j37oNzoU1p'); + $this->analyzeFeatureBuilding($eventSourcingAnalyzer); + } + + private function assertAnalysisAddBuilding( + EventSourcingAnalyzer $eventSourcingAnalyzer, + string $aggregateId, + bool $aggregateConnectionMapOfCommandAddBuildingWithEvent + ): void { + $commandMap = $eventSourcingAnalyzer->commandMap(); + $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + + // commands + $command = $commandMap->current(); + $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); + $commandAddBuildingObjectHash = \spl_object_hash($command); + + // aggregates + $aggregates = $aggregateMap->current(); + $this->assertAggregateBuilding($aggregates, $aggregateId); + + // aggregate connections + $aggregateConnection = $aggregateConnectionMap->current(); + $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, $aggregateConnectionMapOfCommandAddBuildingWithEvent); + $aggregateConnectionCommandAddBuildingObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); + + $this->assertSame($commandAddBuildingObjectHash, $aggregateConnectionCommandAddBuildingObjectHash); + } + + private function assertAnalysisBuildingUser(EventSourcingAnalyzer $eventSourcingAnalyzer): void + { + $commandMap = $eventSourcingAnalyzer->commandMap(); + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + $eventMap = $eventSourcingAnalyzer->eventMap(); + + // commands + $commandMap->next(); + $command = $commandMap->current(); + $this->assertCommandCheckInUser($command); + $commandCheckInUserObjectHash = \spl_object_hash($command); + + // events + $eventMap->rewind(); + $event = $eventMap->current(); + $this->assertEventUserCheckedIn($event); + + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventDoubleCheckInDetected($event); + + // aggregate connections + $aggregateConnectionMap->next(); + $aggregateConnection = $aggregateConnectionMap->current(); + $this->assertAggregateConnectionMapOfCommandCheckInUser($aggregateConnection); + $aggregateConnectionCommandCheckInUserObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); + $this->assertSame($commandCheckInUserObjectHash, $aggregateConnectionCommandCheckInUserObjectHash); + } + + private function assertAnalysisBuilding( + EventSourcingAnalyzer $eventSourcingAnalyzer, + string $id + ): void { + $eventMap = $eventSourcingAnalyzer->eventMap(); + $documentMap = $eventSourcingAnalyzer->documentMap(); + + // events + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventBuildingAdded($event); + + // documents + $documentMap->rewind(); + $document = $documentMap->current(); + $this->assertDocumentBuilding($document, $id); + } + + private function analyzeFeatureBuilding(EventSourcingAnalyzer $eventSourcingAnalyzer): void + { + $commandMap = $eventSourcingAnalyzer->commandMap(); + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + $eventMap = $eventSourcingAnalyzer->eventMap(); + $documentMap = $eventSourcingAnalyzer->documentMap(); + + // commands + $commandMap->next(); + $command = $commandMap->current(); + $this->assertCommandCheckOutUser($command); + $commandCheckOutUserObjectHash = \spl_object_hash($command); + + // events + $eventMap->next(); + $event = $eventMap->current(); + $this->assertEventUserCheckedOut($event); + + // aggregate connections + $aggregateConnectionMap->next(); + $aggregateConnection = $aggregateConnectionMap->current(); + $this->assertAggregateConnectionMapOfCommandCheckOutUser($aggregateConnection); + $aggregateConnectionCommandCheckOutUserObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); + $this->assertSame($commandCheckOutUserObjectHash, $aggregateConnectionCommandCheckOutUserObjectHash); + + // documents + $documentMap->next(); + $document = $documentMap->current(); + $this->assertDocumentUsersInBuilding($document, 't4mMTjg462VRvMW1L6nSGB'); + } + private function assertFeatureCommandMap(VertexMap $commandMap): void { $this->assertCount(3, $commandMap); $command = $commandMap->current(); - $this->assertCommandAddBuilding($command); + $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); $commandMap->next(); $command = $commandMap->current(); @@ -343,9 +579,9 @@ private function assertAggregateBuilding(AggregateType $aggregate, string $id): $this->assertSame('Building ', $aggregate->label()); } - private function assertCommandAddBuilding(CommandType $command): void + private function assertCommandAddBuilding(CommandType $command, string $id): void { - $this->assertSame('9bJ5Y7yuBcfWyei7i2ZSDC', $command->id()); + $this->assertSame($id, $command->id()); $this->assertSame('Add Building', $command->name()); } @@ -363,6 +599,13 @@ private function assertCommandCheckInUser(CommandType $command): void $this->assertSame('Check In User', \trim($command->label())); } + private function assertCommandCheckOutUser(CommandType $command): void + { + $this->assertSame('dkNTGinM6VY1Qu8zyGURU1', $command->id()); + $this->assertSame('Check Out User', $command->name()); + $this->assertSame('Check Out User', \trim($command->label())); + } + private function assertEventUserCheckedIn(EventType $event): void { $this->assertSame('q3thtbbiWsgyRqGadCBLte', $event->id()); @@ -390,4 +633,18 @@ private function assertDocumentName(DocumentType $event): void $this->assertSame('Name', $event->name()); $this->assertSame('Name', $event->label()); } + + private function assertDocumentBuilding(DocumentType $event, string $id): void + { + $this->assertSame($id, $event->id()); + $this->assertSame('Building', $event->name()); + $this->assertSame('Building ', $event->label()); + } + + private function assertDocumentUsersInBuilding(DocumentType $event, string $id): void + { + $this->assertSame($id, $event->id()); + $this->assertSame('Users In Building', $event->name()); + $this->assertSame('Users In Building ', $event->label()); + } } From 5563e0e671c4cb6a43dff23f4c3d59a84ff66093 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Tue, 1 Jun 2021 13:21:40 +0200 Subject: [PATCH 05/18] Move node analysis to own class - This is a small BC break due constructor change and method call --- src/EventSourcingAnalyzer.php | 473 ++---------------- src/EventSourcingGraph.php | 422 ++++++++++++++++ .../NodeToEventSourcingAnalyzer.php | 6 +- tests/EventSourcingAnalyzerTest.php | 194 +++++-- 4 files changed, 600 insertions(+), 495 deletions(-) create mode 100644 src/EventSourcingGraph.php diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index a5e6e2f..faacb10 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -11,28 +11,15 @@ namespace EventEngine\InspectioGraphCody; use EventEngine\InspectioGraph; -use EventEngine\InspectioGraph\Connection\AggregateConnection; use EventEngine\InspectioGraph\Connection\AggregateConnectionAnalyzer; use EventEngine\InspectioGraph\Connection\AggregateConnectionMap; -use EventEngine\InspectioGraph\Connection\FeatureConnection; use EventEngine\InspectioGraph\Connection\FeatureConnectionAnalyzer; use EventEngine\InspectioGraph\Connection\FeatureConnectionMap; use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraph\VertexType; -use EventEngine\InspectioGraphCody\Exception\RuntimeException; final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, AggregateConnectionAnalyzer, FeatureConnectionAnalyzer { - /** - * @var Node - **/ - private $node; - - /** - * @var callable - **/ - private $filterName; - /** * @var VertexMap */ @@ -94,502 +81,110 @@ final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyze private $boundedContextMap; /** - * @var callable + * @var EventSourcingGraph */ - private $metadataFactory; - - public function __construct( - Node $node, - callable $filterName, - ?callable $metadataFactory = null - ) { - $this->node = $node; - $this->filterName = $filterName; - $this->metadataFactory = $metadataFactory; - - $this->analyse($node); // for BC, $node should be removed later - } + private $graph; - public function analyse(Node $node): void + public function __construct(EventSourcingGraph $graph) { - $this->node = $node; + $this->graph = $graph; - // all maps can be analyzed in parallel - $this->commandMap = $this->commandMap()->with(...$this->vertexMapByType(VertexType::TYPE_COMMAND)); - $this->aggregateMap = $this->aggregateMap()->with(...$this->vertexMapByType(VertexType::TYPE_AGGREGATE)); - $this->eventMap = $this->eventMap()->with(...$this->vertexMapByType(VertexType::TYPE_EVENT)); - $this->documentMap = $this->documentMap()->with(...$this->vertexMapByType(VertexType::TYPE_DOCUMENT)); - $this->policyMap = $this->policyMap()->with(...$this->vertexMapByType(VertexType::TYPE_POLICY)); - $this->uiMap = $this->uiMap()->with(...$this->vertexMapByType(VertexType::TYPE_UI)); - $this->externalSystemMap = $this->externalSystemMap()->with(...$this->vertexMapByType(VertexType::TYPE_EXTERNAL_SYSTEM)); - $this->hotSpotMap = $this->hotSpotMap()->with(...$this->vertexMapByType(VertexType::TYPE_HOT_SPOT)); - $this->featureMap = $this->featureMap()->with(...$this->vertexMapByType(VertexType::TYPE_FEATURE)); - $this->boundedContextMap = $this->boundedContextMap()->with(...$this->boundedContextMap()->vertices()); - - // all connection maps can be analyzed in parallel - foreach ($this->determineAggregateConnection() as $aggregateConnection) { - $this->aggregateConnectionMap = $this->aggregateConnectionMap()->with( - $aggregateConnection->aggregate()->id(), - $aggregateConnection - ); - } - - foreach ($this->determineFeatureConnectionMap() as $featureConnection) { - $this->featureConnectionMap = $this->featureConnectionMap()->with( - $featureConnection->feature()->id(), - $featureConnection - ); - } - - $this->commandMap()->rewind(); - $this->aggregateMap()->rewind(); - $this->eventMap()->rewind(); - $this->aggregateConnectionMap()->rewind(); - $this->documentMap()->rewind(); - $this->policyMap()->rewind(); - $this->uiMap()->rewind(); - $this->externalSystemMap()->rewind(); - $this->hotSpotMap()->rewind(); - $this->featureMap()->rewind(); - $this->featureConnectionMap()->rewind(); - $this->boundedContextMap()->rewind(); + $this->commandMap = VertexMap::emptyMap(); + $this->aggregateMap = VertexMap::emptyMap(); + $this->eventMap = VertexMap::emptyMap(); + $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); + $this->documentMap = VertexMap::emptyMap(); + $this->policyMap = VertexMap::emptyMap(); + $this->uiMap = VertexMap::emptyMap(); + $this->externalSystemMap = VertexMap::emptyMap(); + $this->hotSpotMap = VertexMap::emptyMap(); + $this->featureMap = VertexMap::emptyMap(); + $this->featureConnectionMap = FeatureConnectionMap::emptyMap(); + $this->boundedContextMap = VertexMap::emptyMap(); } /** - * @param string $type - * @return Node[] + * @param Node $node + * @return VertexType The vertex type of the provided node from the corresponding map e.g. command map */ - private function filterVerticesByType(string $type): array + public function analyse(Node $node): VertexType { - $vertices = []; - - if ($this->node->type() === $type) { - $vertices[] = $this->node; - } - - $parent = $this->node->parent(); - - if ($parent && $parent->type() === $type) { - $vertices[] = $parent; - } - - foreach ($this->node->sources() as $source) { - if ($source->type() === $type) { - $vertices[] = $source; - } - } - - foreach ($this->node->targets() as $target) { - if ($target->type() === $type) { - $vertices[] = $target; - } - } - - foreach ($this->node->children() as $child) { - if ($child->type() === $type) { - $vertices[] = $child; - } - } - - return $vertices; - } + // all maps can be analyzed in parallel + $this->commandMap = $this->graph->analyseMap($node, $this->commandMap, VertexType::TYPE_COMMAND); + $this->aggregateMap = $this->graph->analyseMap($node, $this->aggregateMap, VertexType::TYPE_AGGREGATE); + $this->eventMap = $this->graph->analyseMap($node, $this->eventMap, VertexType::TYPE_EVENT); + $this->documentMap = $this->graph->analyseMap($node, $this->documentMap, VertexType::TYPE_DOCUMENT); + $this->policyMap = $this->graph->analyseMap($node, $this->policyMap, VertexType::TYPE_POLICY); + $this->uiMap = $this->graph->analyseMap($node, $this->uiMap, VertexType::TYPE_UI); + $this->externalSystemMap = $this->graph->analyseMap($node, $this->externalSystemMap, VertexType::TYPE_EXTERNAL_SYSTEM); + $this->hotSpotMap = $this->graph->analyseMap($node, $this->hotSpotMap, VertexType::TYPE_HOT_SPOT); + $this->featureMap = $this->graph->analyseMap($node, $this->featureMap, VertexType::TYPE_FEATURE); + $this->boundedContextMap = $this->graph->analyseMap($node, $this->boundedContextMap, VertexType::TYPE_BOUNDED_CONTEXT); - /** - * @param string $vertexType - * @param VertexType $node - * @return VertexMap - */ - private function filterVertexTypeWithConnectionOf(string $vertexType, VertexType $node): VertexMap - { - $vertices = VertexMap::emptyMap(); - - switch ($this->node->type()) { - case VertexType::TYPE_AGGREGATE: - foreach ($this->node->sources() as $source) { - if ($source->type() === $vertexType - && ($vertex = $this->vertexFromMap($source)) - ) { - $vertices = $vertices->with($vertex); - } - } - foreach ($this->node->targets() as $target) { - if ($target->type() === $vertexType - && ($vertex = $this->vertexFromMap($target)) - ) { - $vertices = $vertices->with($vertex); - } - } - $parent = $this->node->parent(); - - if (null !== $parent - && $this->node->type() === $vertexType - && $this->areNodesEqual($parent, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($this->node)); - } - break; - case VertexType::TYPE_COMMAND: - case VertexType::TYPE_EVENT: - case VertexType::TYPE_DOCUMENT: - foreach ($this->node->sources() as $source) { - if ($this->node->type() === $vertexType - && $this->areNodesEqual($source, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($this->node)); - } elseif ($parent = $source->parent()) { - if ($source->type() === $vertexType - && $this->areNodesEqual($parent, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($source)); - } - } - } - foreach ($this->node->targets() as $target) { - if ($this->node->type() === $vertexType - && $this->areNodesEqual($target, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($this->node)); - } elseif ($parent = $target->parent()) { - if ($target->type() === $vertexType - && $this->areNodesEqual($parent, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($target)); - } - } - } - $parent = $this->node->parent(); - - if (null !== $parent - && $this->node->type() === $vertexType - && $this->areNodesEqual($parent, $node) - ) { - $vertices = $vertices->with($this->vertexFromMap($this->node)); - } - break; - case VertexType::TYPE_FEATURE: - foreach ($this->node->children() as $child) { - if ($child->type() === $vertexType - && ($vertex = $this->vertexFromMap($child)) - ) { - if ($node instanceof InspectioGraph\FeatureType) { - $vertices = $vertices->with($vertex); - } elseif ($this->isFeatureVertexSourceOrTargetOf($vertex, $node)) { - $vertices = $vertices->with($vertex); - } - } - } - break; - default: - break; - } - - return $vertices; - } + // all connection maps can be analyzed in parallel + $this->aggregateConnectionMap = $this->graph->analyseAggregateConnectionMap($node, $this, $this->aggregateConnectionMap); + $this->featureConnectionMap = $this->graph->analyseFeatureConnectionMap($node, $this, $this->featureConnectionMap); - private function isFeatureVertexSourceOrTargetOf(VertexType $vertexType, VertexType $node): bool - { - foreach ($this->node->children() as $child) { - if ($child->type() === 'edge' - || $this->areNodesIdentical($child, $node) === false - ) { - continue; - } - - foreach ($child->sources() as $childSource) { - if ($this->areNodesIdentical($childSource, $vertexType)) { - return true; - } - } - foreach ($child->targets() as $childTarget) { - if ($this->areNodesIdentical($childTarget, $vertexType)) { - return true; - } - } - } - - return false; + return $this->graph->vertexOfNode($node, $this); } public function commandMap(): VertexMap { - if (null === $this->commandMap) { - $this->commandMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_COMMAND)); - } - return $this->commandMap; } public function eventMap(): VertexMap { - if (null === $this->eventMap) { - $this->eventMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_EVENT)); - } - return $this->eventMap; } public function aggregateMap(): VertexMap { - if (null === $this->aggregateMap) { - $this->aggregateMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_AGGREGATE)); - } - return $this->aggregateMap; } public function aggregateConnectionMap(): AggregateConnectionMap { - if (null === $this->aggregateConnectionMap) { - $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - - foreach ($this->determineAggregateConnection() as $aggregateConnection) { - $this->aggregateConnectionMap = $this->aggregateConnectionMap->with( - $aggregateConnection->aggregate()->id(), - $aggregateConnection - ); - } - } - return $this->aggregateConnectionMap; } - private function determineAggregateConnection(): AggregateConnectionMap - { - $aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - - foreach ($this->filterVerticesByType(VertexType::TYPE_AGGREGATE) as $node) { - $aggregate = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - $name = $aggregate->name(); - - // @phpstan-ignore-next-line - $aggregateConnection = new AggregateConnection($aggregate); - - $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); - $commandVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $aggregate); - $eventVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $aggregate); - $documentVertices = $this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $aggregate); - - $countCommandVertices = \count($commandVertices); - - if ($countCommandVertices > 1) { - throw new RuntimeException( - \sprintf('Multiple command connections to aggregate "%s" found. Can not handle it.', $name) - ); - } - - if ($countCommandVertices === 1) { - $command = $commandVertices->current(); - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); - } elseif (\count($eventVertices) > 0) { - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); - } - - if (\count($documentVertices) > 0) { - $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); - } - - $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); - } - - return $aggregateConnectionMap; - } - public function documentMap(): VertexMap { - if (null === $this->documentMap) { - $this->documentMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_DOCUMENT)); - } - return $this->documentMap; } - private function vertexMapByType(string $type): array - { - return \array_map( - function (Node $vertex) { - return Vertex::fromCodyNode($vertex, $this->filterName, $this->metadataFactory); - }, - $this->filterVerticesByType($type) - ); - } - - private function vertexFromMap(Node $node): ?VertexType - { - $name = ($this->filterName)($node->name()); - - switch ($node->type()) { - case VertexType::TYPE_COMMAND: - if ($this->commandMap()->has($name)) { - return $this->commandMap()->vertex($name); - } - - return null; - case VertexType::TYPE_AGGREGATE: - if ($this->aggregateMap()->has($name)) { - return $this->aggregateMap()->vertex($name); - } - - return null; - case VertexType::TYPE_EVENT: - if ($this->eventMap()->has($name)) { - return $this->eventMap()->vertex($name); - } - - return null; - case VertexType::TYPE_DOCUMENT: - if ($this->documentMap()->has($name)) { - return $this->documentMap()->vertex($name); - } - - return null; - case VertexType::TYPE_POLICY: - if ($this->policyMap()->has($name)) { - return $this->policyMap()->vertex($name); - } - - return null; - case VertexType::TYPE_UI: - if ($this->uiMap()->has($name)) { - return $this->uiMap()->vertex($name); - } - - return null; - case VertexType::TYPE_HOT_SPOT: - if ($this->hotSpotMap()->has($name)) { - return $this->hotSpotMap()->vertex($name); - } - - return null; - case VertexType::TYPE_FEATURE: - if ($this->featureMap()->has($name)) { - return $this->featureMap()->vertex($name); - } - - return null; - case VertexType::TYPE_BOUNDED_CONTEXT: - if ($this->boundedContextMap()->has($name)) { - return $this->boundedContextMap()->vertex($name); - } - - return null; - default: - throw new RuntimeException( - \sprintf('Type "%s" is not supported', $node->type()) - ); - } - } - - private function areNodesEqual(Node $a, VertexType $b): bool - { - $nodeName = ($this->filterName)($a->name()); - - return $nodeName === $b->name() - && $a->type() === $b->type(); - } - - private function areNodesIdentical(Node $a, VertexType $b): bool - { - return $a->type() === $b->type() - && $a->id() === $b->id(); - } - public function policyMap(): VertexMap { - if (null === $this->policyMap) { - $this->policyMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_POLICY)); - } - return $this->policyMap; } public function uiMap(): VertexMap { - if (null === $this->uiMap) { - $this->uiMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_UI)); - } - return $this->uiMap; } public function featureMap(): VertexMap { - if (null === $this->featureMap) { - $this->featureMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_FEATURE)); - } - return $this->featureMap; } public function featureConnectionMap(): FeatureConnectionMap { - if (null === $this->featureConnectionMap) { - $this->featureConnectionMap = FeatureConnectionMap::emptyMap(); - - foreach ($this->determineFeatureConnectionMap() as $featureConnection) { - $this->featureConnectionMap = $this->featureConnectionMap->with( - $featureConnection->feature()->id(), - $featureConnection - ); - } - } - return $this->featureConnectionMap; } - private function determineFeatureConnectionMap(): FeatureConnectionMap - { - $featureConnectionMap = FeatureConnectionMap::emptyMap(); - - foreach ($this->filterVerticesByType(VertexType::TYPE_FEATURE) as $node) { - $feature = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - - // @phpstan-ignore-next-line - $featureConnection = new FeatureConnection($feature); - - $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); - - $featureConnection = $featureConnection - ->withCommands(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_COMMAND, $feature)->vertices()) - ->withEvents(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EVENT, $feature)->vertices()) - ->withAggregates(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_AGGREGATE, $feature)->vertices()) - ->withDocuments(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_DOCUMENT, $feature)->vertices()) - ->withExternalSystems(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_EXTERNAL_SYSTEM, $feature)->vertices()) - ->withHotSpots(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_HOT_SPOT, $feature)->vertices()) - ->withPolicies(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_POLICY, $feature)->vertices()) - ->withUis(...$this->filterVertexTypeWithConnectionOf(VertexType::TYPE_UI, $feature)->vertices()); - - $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); - } - - return $featureConnectionMap; - } - public function boundedContextMap(): VertexMap { - if (null === $this->boundedContextMap) { - $this->boundedContextMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_BOUNDED_CONTEXT)); - } - return $this->boundedContextMap; } public function externalSystemMap(): VertexMap { - if (null === $this->externalSystemMap) { - $this->externalSystemMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_EXTERNAL_SYSTEM)); - } - return $this->externalSystemMap; } public function hotSpotMap(): VertexMap { - if (null === $this->hotSpotMap) { - $this->hotSpotMap = VertexMap::fromVertices(...$this->vertexMapByType(VertexType::TYPE_HOT_SPOT)); - } - return $this->hotSpotMap; } } diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php new file mode 100644 index 0000000..16faacc --- /dev/null +++ b/src/EventSourcingGraph.php @@ -0,0 +1,422 @@ +filterName = $filterName; + $this->metadataFactory = $metadataFactory; + } + + public function analyseMap(Node $node, VertexMap $map, string $vertexType): VertexMap + { + return $map->with(...$this->vertexMapByType($node, $vertexType)); + } + + public function vertexOfNode(Node $node, InspectioGraph\EventSourcingAnalyzer $analyzer): VertexType + { + switch ($node->type()) { + case VertexType::TYPE_COMMAND: + return $analyzer->commandMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_EVENT: + return $analyzer->eventMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_AGGREGATE: + return $analyzer->aggregateMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_FEATURE: + return $analyzer->featureMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_DOCUMENT: + return $analyzer->documentMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_BOUNDED_CONTEXT: + return $analyzer->boundedContextMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_EXTERNAL_SYSTEM: + return $analyzer->externalSystemMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_HOT_SPOT: + return $analyzer->hotSpotMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_POLICY: + return $analyzer->policyMap()->vertex(($this->filterName)($node->name())); + case VertexType::TYPE_UI: + return $analyzer->uiMap()->vertex(($this->filterName)($node->name())); + default: + throw new RuntimeException(\sprintf('Unknown vertex type "%s" provided.', $node->type())); + } + } + + public function analyseAggregateConnectionMap( + Node $node, + InspectioGraph\EventSourcingAnalyzer $analyzer, + AggregateConnectionMap $aggregateConnectionMap + ): AggregateConnectionMap { + foreach ($this->filterVerticesByType($node, VertexType::TYPE_AGGREGATE) as $filteredNode) { + $aggregateFromMap = $this->vertexFromMap($filteredNode, $analyzer); + + if ($aggregateFromMap === null) { + throw new RuntimeException( + 'Provided aggregate node was not found in aggregate map. Did you analyse the aggregate map before?' + ); + } + $aggregate = Vertex::fromCodyNode($filteredNode, $this->filterName, $this->metadataFactory); + $name = $aggregate->name(); + + // @phpstan-ignore-next-line + $aggregateConnection = new AggregateConnection($aggregateFromMap); + + $commandVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_COMMAND, $aggregate, $analyzer); + $eventVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EVENT, $aggregate, $analyzer); + $documentVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_DOCUMENT, $aggregate, $analyzer); + + $countCommandVertices = \count($commandVertices); + + if ($countCommandVertices > 1) { + throw new RuntimeException( + \sprintf('Multiple command connections to aggregate "%s" found. Can not handle it.', $name) + ); + } + + if ($countCommandVertices === 1) { + $command = $commandVertices->current(); + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); + } elseif (\count($eventVertices) > 0) { + // @phpstan-ignore-next-line + $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); + } + + if (\count($documentVertices) > 0) { + $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); + } + + $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); + } + // @phpstan-ignore-next-line + return $aggregateConnectionMap; + } + + public function analyseFeatureConnectionMap( + Node $node, + InspectioGraph\EventSourcingAnalyzer $analyzer, + FeatureConnectionMap $featureConnectionMap + ): FeatureConnectionMap { + foreach ($this->filterVerticesByType($node, VertexType::TYPE_FEATURE) as $filteredNode) { + $featureFromMap = $this->vertexFromMap($filteredNode, $analyzer); + + if ($featureFromMap === null) { + throw new RuntimeException( + 'Provided feature node was not found in feature map. Did you analyse the feature map before?' + ); + } + + $feature = Vertex::fromCodyNode($filteredNode, $this->filterName, $this->metadataFactory); + + // @phpstan-ignore-next-line + $featureConnection = new FeatureConnection($featureFromMap); + + $featureConnection = $featureConnection + ->withCommands( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_COMMAND, $feature, $analyzer)->vertices() + ) + ->withEvents( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EVENT, $feature, $analyzer)->vertices() + ) + ->withAggregates( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_AGGREGATE, $feature, $analyzer)->vertices() + ) + ->withDocuments( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_DOCUMENT, $feature, $analyzer)->vertices() + ) + ->withExternalSystems( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EXTERNAL_SYSTEM, $feature, $analyzer)->vertices() + ) + ->withHotSpots( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_HOT_SPOT, $feature, $analyzer)->vertices() + ) + ->withPolicies( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_POLICY, $feature, $analyzer)->vertices() + ) + ->withUis( + ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_UI, $feature, $analyzer)->vertices() + ); + + $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); + } + + // @phpstan-ignore-next-line + return $featureConnectionMap; + } + + /** + * @return Node[] + */ + private function filterVerticesByType(Node $node, string $type): array + { + $vertices = []; + + if ($node->type() === $type) { + $vertices[] = $node; + } + + $parent = $node->parent(); + + if ($parent && $parent->type() === $type) { + $vertices[] = $parent; + } + + foreach ($node->sources() as $source) { + if ($source->type() === $type) { + $vertices[] = $source; + } + } + + foreach ($node->targets() as $target) { + if ($target->type() === $type) { + $vertices[] = $target; + } + } + + foreach ($node->children() as $child) { + if ($child->type() === $type) { + $vertices[] = $child; + } + } + + return $vertices; + } + + private function filterVertexTypeWithConnectionOf( + Node $node, + string $vertexType, + VertexType $connectionNode, + InspectioGraph\EventSourcingAnalyzer $analyzer + ): VertexMap { + $vertices = VertexMap::emptyMap(); + + switch ($node->type()) { + case VertexType::TYPE_AGGREGATE: + foreach ($node->sources() as $source) { + if ($source->type() === $vertexType + && ($vertex = $this->vertexFromMap($source, $analyzer)) + ) { + $vertices = $vertices->with($vertex); + } + } + foreach ($node->targets() as $target) { + if ($target->type() === $vertexType + && ($vertex = $this->vertexFromMap($target, $analyzer)) + ) { + $vertices = $vertices->with($vertex); + } + } + $parent = $node->parent(); + + if (null !== $parent + && $node->type() === $vertexType + && $this->areNodesEqual($parent, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); + } + break; + case VertexType::TYPE_COMMAND: + case VertexType::TYPE_EVENT: + case VertexType::TYPE_DOCUMENT: + foreach ($node->sources() as $source) { + if ($node->type() === $vertexType + && $this->areNodesEqual($source, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); + } elseif ($parent = $source->parent()) { + if ($source->type() === $vertexType + && $this->areNodesEqual($parent, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($source, $analyzer)); + } + } + } + foreach ($node->targets() as $target) { + if ($node->type() === $vertexType + && $this->areNodesEqual($target, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); + } elseif ($parent = $target->parent()) { + if ($target->type() === $vertexType + && $this->areNodesEqual($parent, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($target, $analyzer)); + } + } + } + $parent = $node->parent(); + + if (null !== $parent + && $node->type() === $vertexType + && $this->areNodesEqual($parent, $connectionNode) + ) { + $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); + } + break; + case VertexType::TYPE_FEATURE: + foreach ($node->children() as $child) { + if ($child->type() === $vertexType + && ($vertex = $this->vertexFromMap($child, $analyzer)) + ) { + if ($connectionNode instanceof InspectioGraph\FeatureType) { + $vertices = $vertices->with($vertex); + } elseif ($this->isFeatureVertexSourceOrTargetOf($node, $vertex, $connectionNode)) { + $vertices = $vertices->with($vertex); + } + } + } + break; + default: + break; + } + + return $vertices; + } + + private function isFeatureVertexSourceOrTargetOf( + Node $node, + VertexType $vertexType, + VertexType $connectionNode + ): bool { + foreach ($node->children() as $child) { + if ($child->type() === 'edge' + || $this->areNodesIdentical($child, $connectionNode) === false + ) { + continue; + } + + foreach ($child->sources() as $childSource) { + if ($this->areNodesIdentical($childSource, $vertexType)) { + return true; + } + } + foreach ($child->targets() as $childTarget) { + if ($this->areNodesIdentical($childTarget, $vertexType)) { + return true; + } + } + } + + return false; + } + + private function vertexMapByType(Node $node, string $type): array + { + return \array_map( + function (Node $vertex) { + return Vertex::fromCodyNode($vertex, $this->filterName, $this->metadataFactory); + }, + $this->filterVerticesByType($node, $type) + ); + } + + private function vertexFromMap(Node $node, InspectioGraph\EventSourcingAnalyzer $analyzer): ?VertexType + { + $name = ($this->filterName)($node->name()); + + switch ($node->type()) { + case VertexType::TYPE_COMMAND: + if ($analyzer->commandMap()->has($name)) { + return $analyzer->commandMap()->vertex($name); + } + + return null; + case VertexType::TYPE_AGGREGATE: + if ($analyzer->aggregateMap()->has($name)) { + return $analyzer->aggregateMap()->vertex($name); + } + + return null; + case VertexType::TYPE_EVENT: + if ($analyzer->eventMap()->has($name)) { + return $analyzer->eventMap()->vertex($name); + } + + return null; + case VertexType::TYPE_DOCUMENT: + if ($analyzer->documentMap()->has($name)) { + return $analyzer->documentMap()->vertex($name); + } + + return null; + case VertexType::TYPE_POLICY: + if ($analyzer->policyMap()->has($name)) { + return $analyzer->policyMap()->vertex($name); + } + + return null; + case VertexType::TYPE_UI: + if ($analyzer->uiMap()->has($name)) { + return $analyzer->uiMap()->vertex($name); + } + + return null; + case VertexType::TYPE_HOT_SPOT: + if ($analyzer->hotSpotMap()->has($name)) { + return $analyzer->hotSpotMap()->vertex($name); + } + + return null; + case VertexType::TYPE_FEATURE: + if ($analyzer->featureMap()->has($name)) { + return $analyzer->featureMap()->vertex($name); + } + + return null; + case VertexType::TYPE_BOUNDED_CONTEXT: + if ($analyzer->boundedContextMap()->has($name)) { + return $analyzer->boundedContextMap()->vertex($name); + } + + return null; + default: + throw new RuntimeException( + \sprintf('Type "%s" is not supported', $node->type()) + ); + } + } + + private function areNodesEqual(Node $a, VertexType $b): bool + { + $nodeName = ($this->filterName)($a->name()); + + return $nodeName === $b->name() + && $a->type() === $b->type(); + } + + private function areNodesIdentical(Node $a, VertexType $b): bool + { + return $a->type() === $b->type() + && $a->id() === $b->id(); + } +} diff --git a/src/Transformator/NodeToEventSourcingAnalyzer.php b/src/Transformator/NodeToEventSourcingAnalyzer.php index 1da2017..04e6594 100644 --- a/src/Transformator/NodeToEventSourcingAnalyzer.php +++ b/src/Transformator/NodeToEventSourcingAnalyzer.php @@ -12,6 +12,7 @@ use EventEngine\InspectioGraphCody\Constraint\Exception\RuntimeException; use EventEngine\InspectioGraphCody\EventSourcingAnalyzer; +use EventEngine\InspectioGraphCody\EventSourcingGraph; use EventEngine\InspectioGraphCody\Node; use EventEngine\InspectioGraphCody\Validator; use OpenCodeModeling\CodeGenerator\Workflow; @@ -48,8 +49,11 @@ public function __invoke(Node $node): \EventEngine\InspectioGraph\EventSourcingA if (false === $this->validator->isValid($node)) { throw new RuntimeException('Graph is invalid.'); } + // TODO needs EventSourcingAnalyzer as input argument + $analyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filterName, $this->metadataFactory)); + $analyzer->analyse($node); - return new EventSourcingAnalyzer($node, $this->filterName, $this->metadataFactory); + return $analyzer; } public static function workflowComponentDescription( diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index e8aa2a9..b0e6362 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -16,7 +16,9 @@ use EventEngine\InspectioGraph\DocumentType; use EventEngine\InspectioGraph\EventType; use EventEngine\InspectioGraph\VertexMap; +use EventEngine\InspectioGraph\VertexType; use EventEngine\InspectioGraphCody\EventSourcingAnalyzer; +use EventEngine\InspectioGraphCody\EventSourcingGraph; use EventEngine\InspectioGraphCody\JsonNode; use PHPUnit\Framework\TestCase; @@ -43,7 +45,9 @@ public function it_returns_command_map_of_command_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $commandMap = $eventSourcingAnalyzer->commandMap(); $this->assertCount(1, $commandMap); @@ -59,7 +63,9 @@ public function it_returns_feature_connection_map_of_command_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); $this->assertCount(1, $featureConnectionMap); @@ -76,19 +82,22 @@ public function it_returns_aggregate_connection_map_of_command_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateConnectionMap); - $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnectionMap->current()); + $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnectionMap->current(), 'buTwEKKNLBBo6WAERYN1Gn'); } private function assertAggregateConnectionMapOfCommandAddBuilding( AggregateConnection $aggregateConnection, - bool $withEvent = false): void - { - $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); + string $aggregateId, + bool $withEvent = false + ): void { + $this->assertAggregateBuilding($aggregateConnection->aggregate(), $aggregateId); $commandMap = $aggregateConnection->commandMap(); $this->assertCount(1, $commandMap); @@ -104,9 +113,9 @@ private function assertAggregateConnectionMapOfCommandAddBuilding( } } - private function assertAggregateConnectionMapOfCommandCheckInUser(AggregateConnection $aggregateConnection): void + private function assertAggregateConnectionMapOfCommandCheckInUser(AggregateConnection $aggregateConnection, string $aggregateId): void { - $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'eiaS8gtsBemMReTNbeNRXj'); + $this->assertAggregateBuilding($aggregateConnection->aggregate(), $aggregateId); $commandMap = $aggregateConnection->commandMap(); $this->assertCount(1, $commandMap); @@ -142,7 +151,9 @@ public function it_returns_aggregate_connection_map_of_event_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); @@ -164,7 +175,9 @@ public function it_returns_aggregate_connection_map_of_document_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'name_vo.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); @@ -189,7 +202,9 @@ public function it_returns_feature_connection_map_of_event_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); $this->assertCount(1, $featureConnectionMap); @@ -214,7 +229,9 @@ public function it_returns_command_map_of_aggregate_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $commandMap = $eventSourcingAnalyzer->commandMap(); $this->assertCount(1, $commandMap); @@ -230,7 +247,9 @@ public function it_returns_event_map_of_aggregate_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $eventMap = $eventSourcingAnalyzer->eventMap(); $this->assertCount(2, $eventMap); @@ -249,7 +268,9 @@ public function it_returns_aggregate_connection_map_of_aggregate_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $this->assertCount(1, $aggregateMap); @@ -286,7 +307,9 @@ public function it_returns_feature_connection_map_of_aggregate_node(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); $this->assertCount(1, $featureConnectionMap); @@ -314,7 +337,8 @@ public function it_returns_feature_connection_map(): void { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + $eventSourcingAnalyzer->analyse($node); $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); @@ -351,13 +375,18 @@ public function it_returns_feature_connection_map(): void } /** + * The last node overrides the previous node. This means that a map overrides any previous node of the same type/name + * from the last analysis. That's why the test uses different ids for the same aggregate. + * * @test */ public function it_analysis_nodes(): void { + $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + // order is important for assertions $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - $eventSourcingAnalyzer = new EventSourcingAnalyzer($node, $this->filter); + $eventSourcingAnalyzer->analyse($node); $this->assertCount(1, $eventSourcingAnalyzer->commandMap()); $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); @@ -389,12 +418,12 @@ public function it_analysis_nodes(): void $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'eiaS8gtsBemMReTNbeNRXj', false); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'eiaS8gtsBemMReTNbeNRXj'); $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); $eventSourcingAnalyzer->analyse($node); $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn'); $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); @@ -413,7 +442,7 @@ public function it_analysis_nodes(): void $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn'); $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); @@ -430,9 +459,9 @@ public function it_analysis_nodes(): void $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); + $this->assertCount(1, $eventSourcingAnalyzer->boundedContextMap()); $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'jKrpwfkdZnT5xMRKMYrgTF', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer); + $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'jKrpwfkdZnT5xMRKMYrgTF'); $this->assertAnalysisBuilding($eventSourcingAnalyzer, 'f9iDWVAz8qT4j37oNzoU1p'); $this->analyzeFeatureBuilding($eventSourcingAnalyzer); } @@ -444,26 +473,31 @@ private function assertAnalysisAddBuilding( ): void { $commandMap = $eventSourcingAnalyzer->commandMap(); $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); + $eventMap = $eventSourcingAnalyzer->eventMap(); + $featureMap = $eventSourcingAnalyzer->featureMap(); $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); // commands - $command = $commandMap->current(); - $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); - $commandAddBuildingObjectHash = \spl_object_hash($command); + $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); // aggregates - $aggregates = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregates, $aggregateId); + $this->assertAggregateBuilding($aggregateMap->current(), $aggregateId); // aggregate connections $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, $aggregateConnectionMapOfCommandAddBuildingWithEvent); - $aggregateConnectionCommandAddBuildingObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); + $this->assertIdenticalVertex($aggregateMap->current(), $aggregateConnection->aggregate()); + $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); + $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, $aggregateId, $aggregateConnectionMapOfCommandAddBuildingWithEvent); - $this->assertSame($commandAddBuildingObjectHash, $aggregateConnectionCommandAddBuildingObjectHash); + // feature connections + $featureConnection = $featureConnectionMap->current(); + $this->assertIdenticalVertex($featureMap->current(), $featureConnection->feature()); + $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); + $this->assertIdenticalVertex($aggregateMap->current(), $featureConnection->aggregateMap()->current()); } - private function assertAnalysisBuildingUser(EventSourcingAnalyzer $eventSourcingAnalyzer): void + private function assertAnalysisBuildingUser(EventSourcingAnalyzer $eventSourcingAnalyzer, string $aggregateId): void { $commandMap = $eventSourcingAnalyzer->commandMap(); $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); @@ -471,25 +505,20 @@ private function assertAnalysisBuildingUser(EventSourcingAnalyzer $eventSourcing // commands $commandMap->next(); - $command = $commandMap->current(); - $this->assertCommandCheckInUser($command); - $commandCheckInUserObjectHash = \spl_object_hash($command); + $this->assertCommandCheckInUser($commandMap->current()); // events $eventMap->rewind(); - $event = $eventMap->current(); - $this->assertEventUserCheckedIn($event); + $this->assertEventUserCheckedIn($eventMap->current()); $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventDoubleCheckInDetected($event); + $this->assertEventDoubleCheckInDetected($eventMap->current()); // aggregate connections $aggregateConnectionMap->next(); $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertAggregateConnectionMapOfCommandCheckInUser($aggregateConnection); - $aggregateConnectionCommandCheckInUserObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); - $this->assertSame($commandCheckInUserObjectHash, $aggregateConnectionCommandCheckInUserObjectHash); + $this->assertAggregateConnectionMapOfCommandCheckInUser($aggregateConnection, $aggregateId); + $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); } private function assertAnalysisBuilding( @@ -501,13 +530,11 @@ private function assertAnalysisBuilding( // events $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventBuildingAdded($event); + $this->assertEventBuildingAdded($eventMap->current()); // documents $documentMap->rewind(); - $document = $documentMap->current(); - $this->assertDocumentBuilding($document, $id); + $this->assertDocumentBuilding($documentMap->current(), $id); } private function analyzeFeatureBuilding(EventSourcingAnalyzer $eventSourcingAnalyzer): void @@ -515,30 +542,87 @@ private function analyzeFeatureBuilding(EventSourcingAnalyzer $eventSourcingAnal $commandMap = $eventSourcingAnalyzer->commandMap(); $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); $eventMap = $eventSourcingAnalyzer->eventMap(); + $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); $documentMap = $eventSourcingAnalyzer->documentMap(); + $featureMap = $eventSourcingAnalyzer->featureMap(); + $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); // commands $commandMap->next(); - $command = $commandMap->current(); - $this->assertCommandCheckOutUser($command); - $commandCheckOutUserObjectHash = \spl_object_hash($command); + $this->assertCommandCheckOutUser($commandMap->current()); // events $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventUserCheckedOut($event); + $this->assertEventUserCheckedOut($eventMap->current()); // aggregate connections $aggregateConnectionMap->next(); $aggregateConnection = $aggregateConnectionMap->current(); $this->assertAggregateConnectionMapOfCommandCheckOutUser($aggregateConnection); - $aggregateConnectionCommandCheckOutUserObjectHash = \spl_object_hash($aggregateConnection->commandMap()->current()); - $this->assertSame($commandCheckOutUserObjectHash, $aggregateConnectionCommandCheckOutUserObjectHash); + + $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); + $this->assertIdenticalVertex($eventMap->current(), $aggregateConnection->eventMap()->current()); // documents $documentMap->next(); - $document = $documentMap->current(); - $this->assertDocumentUsersInBuilding($document, 't4mMTjg462VRvMW1L6nSGB'); + $this->assertDocumentUsersInBuilding($documentMap->current(), 't4mMTjg462VRvMW1L6nSGB'); + + // features + $commandMap->rewind(); + $aggregateConnectionMap->rewind(); + $featureConnection = $featureConnectionMap->current(); + $aggregateConnection = $aggregateConnectionMap->current(); + + $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); + $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); + $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); + $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, 'jKrpwfkdZnT5xMRKMYrgTF', true); + + $commandMap->next(); + $featureConnection->commandMap()->next(); + $this->assertCommandCheckInUser($commandMap->current()); + $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); + + foreach ($featureConnectionMap as $featureConnection) { + $this->assertIdenticalVertex( + $featureMap->vertex($featureConnection->feature()->name()), + $featureConnection->feature() + ); + foreach ($featureConnection->commandMap() as $command) { + $this->assertIdenticalVertex($commandMap->vertex($command->name()), $command); + } + foreach ($featureConnection->eventMap() as $event) { + $this->assertIdenticalVertex($eventMap->vertex($event->name()), $event); + } + foreach ($featureConnection->aggregateMap() as $aggregate) { + $this->assertIdenticalVertex($aggregateMap->vertex($aggregate->name()), $aggregate); + } + foreach ($featureConnection->documentMap() as $document) { + $this->assertIdenticalVertex($documentMap->vertex($document->name()), $document); + } + } + + foreach ($aggregateConnectionMap as $aggregateConnection) { + $this->assertIdenticalVertex( + $aggregateMap->vertex($aggregateConnection->aggregate()->name()), + $aggregateConnection->aggregate() + ); + + foreach ($aggregateConnection->commandMap() as $command) { + $this->assertIdenticalVertex($commandMap->vertex($command->name()), $command); + } + foreach ($aggregateConnection->eventMap() as $event) { + $this->assertIdenticalVertex($eventMap->vertex($event->name()), $event); + } + } + } + + private function assertIdenticalVertex(VertexType $a, VertexType $b): void + { + $this->assertSame($a->type(), $b->type(), 'The type does not match'); + $this->assertSame($a->name(), $b->name(), 'The name does not match'); + $this->assertSame($a->id(), $b->id(), 'The ids does not match'); + $this->assertSame(\spl_object_hash($a), \spl_object_hash($b), 'The objects are not identical'); } private function assertFeatureCommandMap(VertexMap $commandMap): void From 51746ee482d7665a93d19ad941219be8146fc133 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 9 Jul 2021 09:47:11 +0200 Subject: [PATCH 06/18] Set minimum PHP version to 7.4 --- .github/workflows/integration.yml | 6 +----- .php_cs | 16 ++++++++++++++-- composer.json | 4 ++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 48292ad..5c1f111 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -7,14 +7,10 @@ jobs: fail-fast: false matrix: php-version: - - "7.3" - "7.4" + - "8.0" os: [ubuntu-latest] experimental: [false] - include: - - php-version: "8.0" - os: ubuntu-latest - experimental: true runs-on: ${{ matrix.os }} name: PHP ${{ matrix.php-version }} Test on ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} diff --git a/.php_cs b/.php_cs index 363296d..aa91e55 100644 --- a/.php_cs +++ b/.php_cs @@ -1,9 +1,21 @@ getFinder()->in(__DIR__); +$finder = $config->getFinder(); + +$finder->exclude('vendor'); +$finder->in(__DIR__); +$finder->append(['.php_cs']); -$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__; +$cacheDir = \getenv('TRAVIS') ? \getenv('HOME') . '/.php-cs-fixer' : __DIR__; $config->setCacheFile($cacheDir . '/.php_cs.cache'); diff --git a/composer.json b/composer.json index 68caa68..80ac1cf 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,8 @@ } }, "require": { - "php": "^7.3 || ^8.0", - "event-engine/php-inspectio-graph": "dev-feature/add-missing-types", + "php": "^7.4 || ^8.0", + "event-engine/php-inspectio-graph": "dev-feature/simplify-connections", "ext-json": "*" }, "require-dev": { From 39f409bbbb56d2f52271d025ba6ea285824b1e47 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 9 Jul 2021 09:48:30 +0200 Subject: [PATCH 07/18] Use vertex connection and simplify analyzing the graph --- phpstan.neon.dist | 1 - src/EventSourcingAnalyzer.php | 188 ++--- src/EventSourcingGraph.php | 408 ++--------- src/Metadata/Aggregate.php | 49 -- src/Metadata/Command.php | 61 -- src/Metadata/Event.php | 61 -- src/Metadata/NodeJsonMetadataFactory.php | 50 -- src/Metadata/Policy.php | 46 -- src/Vertex.php | 34 +- tests/EventSourcingAnalyzerTest.php | 836 ++++++++--------------- 10 files changed, 422 insertions(+), 1312 deletions(-) delete mode 100644 src/Metadata/Aggregate.php delete mode 100644 src/Metadata/Command.php delete mode 100644 src/Metadata/Event.php delete mode 100644 src/Metadata/NodeJsonMetadataFactory.php delete mode 100644 src/Metadata/Policy.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5ff44e5..472c64c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,4 +3,3 @@ parameters: paths: - src/ ignoreErrors: - - '#.*expects EventEngine\\InspectioGraph\\[a-zA-Z0-9\\_]+\, EventEngine\\InspectioGraph\\VertexType given.#' diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index faacb10..d0065a5 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -10,181 +10,127 @@ namespace EventEngine\InspectioGraphCody; +use Countable; use EventEngine\InspectioGraph; -use EventEngine\InspectioGraph\Connection\AggregateConnectionAnalyzer; -use EventEngine\InspectioGraph\Connection\AggregateConnectionMap; -use EventEngine\InspectioGraph\Connection\FeatureConnectionAnalyzer; -use EventEngine\InspectioGraph\Connection\FeatureConnectionMap; -use EventEngine\InspectioGraph\VertexMap; +use EventEngine\InspectioGraph\VertexConnection; +use EventEngine\InspectioGraph\VertexConnectionMap; use EventEngine\InspectioGraph\VertexType; +use Iterator; -final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, AggregateConnectionAnalyzer, FeatureConnectionAnalyzer +final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyzer, Countable, Iterator { - /** - * @var VertexMap - */ - private $commandMap; - - /** - * @var VertexMap - */ - private $aggregateMap; - - /** - * @var AggregateConnectionMap - */ - private $aggregateConnectionMap; + use InspectioGraph\EventSourcingGraph; - /** - * @var VertexMap - */ - private $eventMap; + private VertexConnectionMap $identityConnectionMap; + private EventSourcingGraph $graph; - /** - * @var VertexMap - */ - private $documentMap; - - /** - * @var VertexMap - */ - private $policyMap; - - /** - * @var VertexMap - */ - private $uiMap; - - /** - * @var VertexMap - */ - private $externalSystemMap; - - /** - * @var VertexMap - */ - private $hotSpotMap; - - /** - * @var VertexMap - */ - private $featureMap; + public function __construct(EventSourcingGraph $graph) + { + $this->graph = $graph; + $this->identityConnectionMap = VertexConnectionMap::emptyMap(); + } /** - * @var FeatureConnectionMap + * @param Node $node + * @return InspectioGraph\VertexConnection The vertex with it's connections of provided node */ - private $featureConnectionMap; + public function analyse(Node $node): InspectioGraph\VertexConnection + { + $this->identityConnectionMap = $this->graph->analyseConnections($node, $this->identityConnectionMap); - /** - * @var VertexMap - */ - private $boundedContextMap; + return $this->identityConnectionMap->connection($node->id()); + } - /** - * @var EventSourcingGraph - */ - private $graph; + public function commandMap(): VertexConnectionMap + { + return $this->identityConnectionMap->filterByType(VertexType::TYPE_COMMAND); + } - public function __construct(EventSourcingGraph $graph) + public function eventMap(): VertexConnectionMap { - $this->graph = $graph; + return $this->identityConnectionMap->filterByType(VertexType::TYPE_EVENT); + } - $this->commandMap = VertexMap::emptyMap(); - $this->aggregateMap = VertexMap::emptyMap(); - $this->eventMap = VertexMap::emptyMap(); - $this->aggregateConnectionMap = AggregateConnectionMap::emptyMap(); - $this->documentMap = VertexMap::emptyMap(); - $this->policyMap = VertexMap::emptyMap(); - $this->uiMap = VertexMap::emptyMap(); - $this->externalSystemMap = VertexMap::emptyMap(); - $this->hotSpotMap = VertexMap::emptyMap(); - $this->featureMap = VertexMap::emptyMap(); - $this->featureConnectionMap = FeatureConnectionMap::emptyMap(); - $this->boundedContextMap = VertexMap::emptyMap(); + public function aggregateMap(): VertexConnectionMap + { + return $this->identityConnectionMap->filterByType(VertexType::TYPE_AGGREGATE); } - /** - * @param Node $node - * @return VertexType The vertex type of the provided node from the corresponding map e.g. command map - */ - public function analyse(Node $node): VertexType + public function documentMap(): VertexConnectionMap { - // all maps can be analyzed in parallel - $this->commandMap = $this->graph->analyseMap($node, $this->commandMap, VertexType::TYPE_COMMAND); - $this->aggregateMap = $this->graph->analyseMap($node, $this->aggregateMap, VertexType::TYPE_AGGREGATE); - $this->eventMap = $this->graph->analyseMap($node, $this->eventMap, VertexType::TYPE_EVENT); - $this->documentMap = $this->graph->analyseMap($node, $this->documentMap, VertexType::TYPE_DOCUMENT); - $this->policyMap = $this->graph->analyseMap($node, $this->policyMap, VertexType::TYPE_POLICY); - $this->uiMap = $this->graph->analyseMap($node, $this->uiMap, VertexType::TYPE_UI); - $this->externalSystemMap = $this->graph->analyseMap($node, $this->externalSystemMap, VertexType::TYPE_EXTERNAL_SYSTEM); - $this->hotSpotMap = $this->graph->analyseMap($node, $this->hotSpotMap, VertexType::TYPE_HOT_SPOT); - $this->featureMap = $this->graph->analyseMap($node, $this->featureMap, VertexType::TYPE_FEATURE); - $this->boundedContextMap = $this->graph->analyseMap($node, $this->boundedContextMap, VertexType::TYPE_BOUNDED_CONTEXT); + return $this->identityConnectionMap->filterByType(VertexType::TYPE_DOCUMENT); + } - // all connection maps can be analyzed in parallel - $this->aggregateConnectionMap = $this->graph->analyseAggregateConnectionMap($node, $this, $this->aggregateConnectionMap); - $this->featureConnectionMap = $this->graph->analyseFeatureConnectionMap($node, $this, $this->featureConnectionMap); + public function policyMap(): VertexConnectionMap + { + return $this->identityConnectionMap->filterByType(VertexType::TYPE_POLICY); + } - return $this->graph->vertexOfNode($node, $this); + public function uiMap(): VertexConnectionMap + { + return $this->identityConnectionMap->filterByType(VertexType::TYPE_UI); } - public function commandMap(): VertexMap + public function featureMap(): VertexConnectionMap { - return $this->commandMap; + return $this->identityConnectionMap->filterByType(VertexType::TYPE_FEATURE); } - public function eventMap(): VertexMap + public function boundedContextMap(): VertexConnectionMap { - return $this->eventMap; + return $this->identityConnectionMap->filterByType(VertexType::TYPE_BOUNDED_CONTEXT); } - public function aggregateMap(): VertexMap + public function externalSystemMap(): VertexConnectionMap { - return $this->aggregateMap; + return $this->identityConnectionMap->filterByType(VertexType::TYPE_EXTERNAL_SYSTEM); } - public function aggregateConnectionMap(): AggregateConnectionMap + public function hotSpotMap(): VertexConnectionMap { - return $this->aggregateConnectionMap; + return $this->identityConnectionMap->filterByType(VertexType::TYPE_HOT_SPOT); } - public function documentMap(): VertexMap + public function has(string $id): bool { - return $this->documentMap; + return $this->identityConnectionMap->has($id); } - public function policyMap(): VertexMap + public function connection(string $id): VertexConnection { - return $this->policyMap; + return $this->identityConnectionMap->connection($id); } - public function uiMap(): VertexMap + public function count(): int { - return $this->uiMap; + return \count($this->identityConnectionMap); } - public function featureMap(): VertexMap + public function rewind(): void { - return $this->featureMap; + $this->identityConnectionMap->rewind(); } - public function featureConnectionMap(): FeatureConnectionMap + public function key(): string { - return $this->featureConnectionMap; + return $this->identityConnectionMap->key(); } - public function boundedContextMap(): VertexMap + public function next(): void { - return $this->boundedContextMap; + $this->identityConnectionMap->next(); } - public function externalSystemMap(): VertexMap + public function valid(): bool { - return $this->externalSystemMap; + return $this->identityConnectionMap->valid(); } - public function hotSpotMap(): VertexMap + /** + * @return VertexConnection|false|mixed + */ + public function current() { - return $this->hotSpotMap; + return $this->identityConnectionMap->current(); } } diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index 16faacc..bd1cb98 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -11,16 +11,12 @@ namespace EventEngine\InspectioGraphCody; use EventEngine\InspectioGraph; -use EventEngine\InspectioGraph\Connection\AggregateConnection; -use EventEngine\InspectioGraph\Connection\AggregateConnectionMap; -use EventEngine\InspectioGraph\Connection\FeatureConnection; -use EventEngine\InspectioGraph\Connection\FeatureConnectionMap; -use EventEngine\InspectioGraph\VertexMap; use EventEngine\InspectioGraph\VertexType; -use EventEngine\InspectioGraphCody\Exception\RuntimeException; final class EventSourcingGraph { + use InspectioGraph\EventSourcingGraph; + /** * @var callable **/ @@ -39,384 +35,66 @@ public function __construct( $this->metadataFactory = $metadataFactory; } - public function analyseMap(Node $node, VertexMap $map, string $vertexType): VertexMap - { - return $map->with(...$this->vertexMapByType($node, $vertexType)); - } - - public function vertexOfNode(Node $node, InspectioGraph\EventSourcingAnalyzer $analyzer): VertexType - { - switch ($node->type()) { - case VertexType::TYPE_COMMAND: - return $analyzer->commandMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_EVENT: - return $analyzer->eventMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_AGGREGATE: - return $analyzer->aggregateMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_FEATURE: - return $analyzer->featureMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_DOCUMENT: - return $analyzer->documentMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_BOUNDED_CONTEXT: - return $analyzer->boundedContextMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_EXTERNAL_SYSTEM: - return $analyzer->externalSystemMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_HOT_SPOT: - return $analyzer->hotSpotMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_POLICY: - return $analyzer->policyMap()->vertex(($this->filterName)($node->name())); - case VertexType::TYPE_UI: - return $analyzer->uiMap()->vertex(($this->filterName)($node->name())); - default: - throw new RuntimeException(\sprintf('Unknown vertex type "%s" provided.', $node->type())); - } - } - - public function analyseAggregateConnectionMap( - Node $node, - InspectioGraph\EventSourcingAnalyzer $analyzer, - AggregateConnectionMap $aggregateConnectionMap - ): AggregateConnectionMap { - foreach ($this->filterVerticesByType($node, VertexType::TYPE_AGGREGATE) as $filteredNode) { - $aggregateFromMap = $this->vertexFromMap($filteredNode, $analyzer); - - if ($aggregateFromMap === null) { - throw new RuntimeException( - 'Provided aggregate node was not found in aggregate map. Did you analyse the aggregate map before?' - ); - } - $aggregate = Vertex::fromCodyNode($filteredNode, $this->filterName, $this->metadataFactory); - $name = $aggregate->name(); - - // @phpstan-ignore-next-line - $aggregateConnection = new AggregateConnection($aggregateFromMap); - - $commandVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_COMMAND, $aggregate, $analyzer); - $eventVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EVENT, $aggregate, $analyzer); - $documentVertices = $this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_DOCUMENT, $aggregate, $analyzer); - - $countCommandVertices = \count($commandVertices); - - if ($countCommandVertices > 1) { - throw new RuntimeException( - \sprintf('Multiple command connections to aggregate "%s" found. Can not handle it.', $name) - ); - } - - if ($countCommandVertices === 1) { - $command = $commandVertices->current(); - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withCommandEvents($command, ...$eventVertices->vertices()); - } elseif (\count($eventVertices) > 0) { - // @phpstan-ignore-next-line - $aggregateConnection = $aggregateConnection->withEvents(...$eventVertices->vertices()); - } - - if (\count($documentVertices) > 0) { - $aggregateConnection = $aggregateConnection->withDocuments(...$documentVertices->vertices()); - } - - $aggregateConnectionMap = $aggregateConnectionMap->with($aggregate->id(), $aggregateConnection); - } - // @phpstan-ignore-next-line - return $aggregateConnectionMap; - } - - public function analyseFeatureConnectionMap( + public function analyseConnections( Node $node, - InspectioGraph\EventSourcingAnalyzer $analyzer, - FeatureConnectionMap $featureConnectionMap - ): FeatureConnectionMap { - foreach ($this->filterVerticesByType($node, VertexType::TYPE_FEATURE) as $filteredNode) { - $featureFromMap = $this->vertexFromMap($filteredNode, $analyzer); + InspectioGraph\VertexConnectionMap $vertexConnectionMap + ): InspectioGraph\VertexConnectionMap { + $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - if ($featureFromMap === null) { - throw new RuntimeException( - 'Provided feature node was not found in feature map. Did you analyse the feature map before?' - ); + foreach ($node->targets() as $target) { + if ($target->type() === 'edge') { + continue; } - - $feature = Vertex::fromCodyNode($filteredNode, $this->filterName, $this->metadataFactory); - - // @phpstan-ignore-next-line - $featureConnection = new FeatureConnection($featureFromMap); - - $featureConnection = $featureConnection - ->withCommands( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_COMMAND, $feature, $analyzer)->vertices() - ) - ->withEvents( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EVENT, $feature, $analyzer)->vertices() - ) - ->withAggregates( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_AGGREGATE, $feature, $analyzer)->vertices() - ) - ->withDocuments( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_DOCUMENT, $feature, $analyzer)->vertices() - ) - ->withExternalSystems( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_EXTERNAL_SYSTEM, $feature, $analyzer)->vertices() - ) - ->withHotSpots( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_HOT_SPOT, $feature, $analyzer)->vertices() - ) - ->withPolicies( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_POLICY, $feature, $analyzer)->vertices() - ) - ->withUis( - ...$this->filterVertexTypeWithConnectionOf($node, VertexType::TYPE_UI, $feature, $analyzer)->vertices() - ); - - $featureConnectionMap = $featureConnectionMap->with($feature->id(), $featureConnection); - } - - // @phpstan-ignore-next-line - return $featureConnectionMap; - } - - /** - * @return Node[] - */ - private function filterVerticesByType(Node $node, string $type): array - { - $vertices = []; - - if ($node->type() === $type) { - $vertices[] = $node; - } - - $parent = $node->parent(); - - if ($parent && $parent->type() === $type) { - $vertices[] = $parent; + $targetIdentity = Vertex::fromCodyNode($target, $this->filterName, $this->metadataFactory); + $vertexConnectionMap = $this->addConnection($identity, $targetIdentity, $vertexConnectionMap); + $vertexConnectionMap = $this->addParent($target, $targetIdentity, $vertexConnectionMap); } foreach ($node->sources() as $source) { - if ($source->type() === $type) { - $vertices[] = $source; - } - } - - foreach ($node->targets() as $target) { - if ($target->type() === $type) { - $vertices[] = $target; + if ($source->type() === 'edge') { + continue; } + $sourceIdentity = Vertex::fromCodyNode($source, $this->filterName, $this->metadataFactory); + $vertexConnectionMap = $this->addConnection($sourceIdentity, $identity, $vertexConnectionMap); + $vertexConnectionMap = $this->addParent($source, $sourceIdentity, $vertexConnectionMap); } foreach ($node->children() as $child) { - if ($child->type() === $type) { - $vertices[] = $child; + if ($child->type() === 'edge') { + continue; } + $vertexConnectionMap = $this->addParentConnection( + Vertex::fromCodyNode($child, $this->filterName, $this->metadataFactory), + $identity, + $vertexConnectionMap + ); } - return $vertices; + return $this->addParent($node, $identity, $vertexConnectionMap); } - private function filterVertexTypeWithConnectionOf( + private function addParent( Node $node, - string $vertexType, - VertexType $connectionNode, - InspectioGraph\EventSourcingAnalyzer $analyzer - ): VertexMap { - $vertices = VertexMap::emptyMap(); - - switch ($node->type()) { - case VertexType::TYPE_AGGREGATE: - foreach ($node->sources() as $source) { - if ($source->type() === $vertexType - && ($vertex = $this->vertexFromMap($source, $analyzer)) - ) { - $vertices = $vertices->with($vertex); - } - } - foreach ($node->targets() as $target) { - if ($target->type() === $vertexType - && ($vertex = $this->vertexFromMap($target, $analyzer)) - ) { - $vertices = $vertices->with($vertex); - } - } - $parent = $node->parent(); - - if (null !== $parent - && $node->type() === $vertexType - && $this->areNodesEqual($parent, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); - } - break; - case VertexType::TYPE_COMMAND: - case VertexType::TYPE_EVENT: - case VertexType::TYPE_DOCUMENT: - foreach ($node->sources() as $source) { - if ($node->type() === $vertexType - && $this->areNodesEqual($source, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); - } elseif ($parent = $source->parent()) { - if ($source->type() === $vertexType - && $this->areNodesEqual($parent, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($source, $analyzer)); - } - } - } - foreach ($node->targets() as $target) { - if ($node->type() === $vertexType - && $this->areNodesEqual($target, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); - } elseif ($parent = $target->parent()) { - if ($target->type() === $vertexType - && $this->areNodesEqual($parent, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($target, $analyzer)); - } - } - } - $parent = $node->parent(); - - if (null !== $parent - && $node->type() === $vertexType - && $this->areNodesEqual($parent, $connectionNode) - ) { - $vertices = $vertices->with($this->vertexFromMap($node, $analyzer)); - } - break; - case VertexType::TYPE_FEATURE: - foreach ($node->children() as $child) { - if ($child->type() === $vertexType - && ($vertex = $this->vertexFromMap($child, $analyzer)) - ) { - if ($connectionNode instanceof InspectioGraph\FeatureType) { - $vertices = $vertices->with($vertex); - } elseif ($this->isFeatureVertexSourceOrTargetOf($node, $vertex, $connectionNode)) { - $vertices = $vertices->with($vertex); - } - } - } - break; - default: - break; - } - - return $vertices; - } - - private function isFeatureVertexSourceOrTargetOf( - Node $node, - VertexType $vertexType, - VertexType $connectionNode - ): bool { - foreach ($node->children() as $child) { - if ($child->type() === 'edge' - || $this->areNodesIdentical($child, $connectionNode) === false + VertexType $nodeIdentity, + InspectioGraph\VertexConnectionMap $vertexConnectionMap + ): InspectioGraph\VertexConnectionMap { + if (($parent = $node->parent()) + && $parent->type() !== 'layer' + ) { + $parentIdentity = Vertex::fromCodyNode($parent, $this->filterName, $this->metadataFactory); + $vertexConnectionMap = $this->addParentConnection($nodeIdentity, $parentIdentity, $vertexConnectionMap); + + if (($parentParents = $parent->parent()) + && $parentParents->type() !== 'layer' ) { - continue; - } - - foreach ($child->sources() as $childSource) { - if ($this->areNodesIdentical($childSource, $vertexType)) { - return true; - } - } - foreach ($child->targets() as $childTarget) { - if ($this->areNodesIdentical($childTarget, $vertexType)) { - return true; - } - } - } - - return false; - } - - private function vertexMapByType(Node $node, string $type): array - { - return \array_map( - function (Node $vertex) { - return Vertex::fromCodyNode($vertex, $this->filterName, $this->metadataFactory); - }, - $this->filterVerticesByType($node, $type) - ); - } - - private function vertexFromMap(Node $node, InspectioGraph\EventSourcingAnalyzer $analyzer): ?VertexType - { - $name = ($this->filterName)($node->name()); - - switch ($node->type()) { - case VertexType::TYPE_COMMAND: - if ($analyzer->commandMap()->has($name)) { - return $analyzer->commandMap()->vertex($name); - } - - return null; - case VertexType::TYPE_AGGREGATE: - if ($analyzer->aggregateMap()->has($name)) { - return $analyzer->aggregateMap()->vertex($name); - } - - return null; - case VertexType::TYPE_EVENT: - if ($analyzer->eventMap()->has($name)) { - return $analyzer->eventMap()->vertex($name); - } - - return null; - case VertexType::TYPE_DOCUMENT: - if ($analyzer->documentMap()->has($name)) { - return $analyzer->documentMap()->vertex($name); - } - - return null; - case VertexType::TYPE_POLICY: - if ($analyzer->policyMap()->has($name)) { - return $analyzer->policyMap()->vertex($name); - } - - return null; - case VertexType::TYPE_UI: - if ($analyzer->uiMap()->has($name)) { - return $analyzer->uiMap()->vertex($name); - } - - return null; - case VertexType::TYPE_HOT_SPOT: - if ($analyzer->hotSpotMap()->has($name)) { - return $analyzer->hotSpotMap()->vertex($name); - } - - return null; - case VertexType::TYPE_FEATURE: - if ($analyzer->featureMap()->has($name)) { - return $analyzer->featureMap()->vertex($name); - } - - return null; - case VertexType::TYPE_BOUNDED_CONTEXT: - if ($analyzer->boundedContextMap()->has($name)) { - return $analyzer->boundedContextMap()->vertex($name); - } - - return null; - default: - throw new RuntimeException( - \sprintf('Type "%s" is not supported', $node->type()) + $vertexConnectionMap = $this->addParentConnection( + $parentIdentity, + Vertex::fromCodyNode($parentParents, $this->filterName, $this->metadataFactory), + $vertexConnectionMap ); + } } - } - - private function areNodesEqual(Node $a, VertexType $b): bool - { - $nodeName = ($this->filterName)($a->name()); - - return $nodeName === $b->name() - && $a->type() === $b->type(); - } - private function areNodesIdentical(Node $a, VertexType $b): bool - { - return $a->type() === $b->type() - && $a->id() === $b->id(); + return $vertexConnectionMap; } } diff --git a/src/Metadata/Aggregate.php b/src/Metadata/Aggregate.php deleted file mode 100644 index ba26f31..0000000 --- a/src/Metadata/Aggregate.php +++ /dev/null @@ -1,49 +0,0 @@ -schema = NodeJsonMetadataFactory::encodeJson($data['schema']); - } - - return $self; - } - - public function schema(): ?string - { - return $this->schema; - } -} diff --git a/src/Metadata/Command.php b/src/Metadata/Command.php deleted file mode 100644 index c1d7545..0000000 --- a/src/Metadata/Command.php +++ /dev/null @@ -1,61 +0,0 @@ -newAggregate = $data['newAggregate'] ?? false; - - if (! empty($data['schema'])) { - $self->schema = NodeJsonMetadataFactory::encodeJson($data['schema']); - } - - return $self; - } - - public function newAggregate(): bool - { - return $this->newAggregate; - } - - public function schema(): ?string - { - return $this->schema; - } -} diff --git a/src/Metadata/Event.php b/src/Metadata/Event.php deleted file mode 100644 index 3cb0d85..0000000 --- a/src/Metadata/Event.php +++ /dev/null @@ -1,61 +0,0 @@ -public = $data['public'] ?? false; - - if (! empty($data['schema'])) { - $self->schema = NodeJsonMetadataFactory::encodeJson($data['schema']); - } - - return $self; - } - - public function public(): bool - { - return $this->public; - } - - public function schema(): ?string - { - return $this->schema; - } -} diff --git a/src/Metadata/NodeJsonMetadataFactory.php b/src/Metadata/NodeJsonMetadataFactory.php deleted file mode 100644 index 0e2293b..0000000 --- a/src/Metadata/NodeJsonMetadataFactory.php +++ /dev/null @@ -1,50 +0,0 @@ -metadata(); - - switch ($vertex->type()) { - case VertexType::TYPE_COMMAND: - return Command::fromJsonMetadata($metadata); - case VertexType::TYPE_AGGREGATE: - return Aggregate::fromJsonMetadata($metadata); - case VertexType::TYPE_EVENT: - return Event::fromJsonMetadata($metadata); - default: - throw new RuntimeException(\sprintf('Given type "%s" is not supported', $vertex->type())); - } - } - - public static function decodeJson(string $json): array - { - return \json_decode($json, true, 512, \JSON_BIGINT_AS_STRING | \JSON_THROW_ON_ERROR); - } - - public static function encodeJson(array $json): string - { - $flags = \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_PRESERVE_ZERO_FRACTION | \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT; - - return \json_encode($json, $flags); - } -} diff --git a/src/Metadata/Policy.php b/src/Metadata/Policy.php deleted file mode 100644 index e24e030..0000000 --- a/src/Metadata/Policy.php +++ /dev/null @@ -1,46 +0,0 @@ -streams = $data['streams'] ?? null; - } - - return $self; - } - - public function streams(): array - { - return $this->streams; - } -} diff --git a/src/Vertex.php b/src/Vertex.php index 92d4d64..76c02ac 100644 --- a/src/Vertex.php +++ b/src/Vertex.php @@ -143,31 +143,37 @@ public function type(): string return $this->type; } - /** - * Raw vertex label - * - * @return string - */ public function label(): string { return $this->label; } - /** - * Filtered label for name - * - * @return string - */ public function name(): string { return $this->name; } - /** - * @return string - */ - public function metadata(): string + public function metadata(): ?string { return $this->metadata; } + + public function merge(VertexType $vertex): void + { + if ($vertex->id() !== $this->id()) { + throw new RuntimeException( + \sprintf('Can not merge vertex due different ids. Id is "%s" but got "%s"', $this->id, $vertex->id()) + ); + } + if ($vertex->type() !== $this->type()) { + throw new RuntimeException( + \sprintf('Can not merge vertex due different types. Type is "%s" but got "%s"', $this->type, $vertex->type()) + ); + } + + $this->metadataInstance = $vertex->metadataInstance(); + $this->metadata = $vertex->metadata(); + $this->name = $vertex->name(); + $this->label = $vertex->label(); + } } diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index b0e6362..1a7dd55 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -12,10 +12,9 @@ use EventEngine\InspectioGraph\AggregateType; use EventEngine\InspectioGraph\CommandType; -use EventEngine\InspectioGraph\Connection\AggregateConnection; use EventEngine\InspectioGraph\DocumentType; use EventEngine\InspectioGraph\EventType; -use EventEngine\InspectioGraph\VertexMap; +use EventEngine\InspectioGraph\FeatureType; use EventEngine\InspectioGraph\VertexType; use EventEngine\InspectioGraphCody\EventSourcingAnalyzer; use EventEngine\InspectioGraphCody\EventSourcingGraph; @@ -25,6 +24,20 @@ final class EventSourcingAnalyzerTest extends TestCase { private const FILES_DIR = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR; + private const ID_FEATURE = 'stW5qRRPsQbowcqux7M2QX'; + + private const ID_ADD_BUILDING = '9bJ5Y7yuBcfWyei7i2ZSDC'; + private const ID_ADD_BUILDING_AGGREGATE = 'buTwEKKNLBBo6WAERYN1Gn'; + private const ID_BUILDING_ADDED = 'tF2ZuZCXsdQMhRmRXydfuW'; + + private const ID_CHECK_IN_USER = 'aKvhibi95v18MKjNjb6tL3'; + private const ID_CHECK_IN_USER_AGGREGATE = 'eiaS8gtsBemMReTNbeNRXj'; + private const ID_USER_CHECKED_IN = 'q3thtbbiWsgyRqGadCBLte'; + private const ID_DOUBLE_CHECKED_IN_DETECTED = '8H79vCoLa3Y2RrpVy7ZMYE'; + + private const ID_CHECK_OUT_USER = 'aKvhibi95v18MKjNjb6tL3'; + private const ID_CHECK_OUT_USER_AGGREGATE = 'eiaS8gtsBemMReTNbeNRXj'; + private const ID_USER_CHECKED_OUT = 'q3thtbbiWsgyRqGadCBLte'; /** * @var callable @@ -41,579 +54,326 @@ public function setUp(): void /** * @test */ - public function it_returns_command_map_of_command_node(): void - { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); - - $commandMap = $eventSourcingAnalyzer->commandMap(); - - $this->assertCount(1, $commandMap); - $command = $commandMap->current(); - - $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); - } - - /** - * @test - */ - public function it_returns_feature_connection_map_of_command_node(): void + public function it_analysis_nodes(): void { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); - - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); - - $this->assertCount(1, $featureConnectionMap); - $featureConnection = $featureConnectionMap->current(); + $analyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $this->assertCommandAddBuilding($featureConnection->commandMap()->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - $this->assertAggregateBuilding($featureConnection->aggregateMap()->current(), 'buTwEKKNLBBo6WAERYN1Gn'); - } - - /** - * @test - */ - public function it_returns_aggregate_connection_map_of_command_node(): void - { + // order is important for assertions $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(1, $analyzer->commandMap()); + $this->assertCount(1, $analyzer->aggregateMap()); + $this->assertCount(0, $analyzer->eventMap()); + $this->assertCount(0, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertCommandAddBuilding($identityConnection->identity(), self::ID_ADD_BUILDING); + $this->assertAnalysisAddBuilding($analyzer, false); - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); - - $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - - $this->assertCount(1, $aggregateConnectionMap); - - $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnectionMap->current(), 'buTwEKKNLBBo6WAERYN1Gn'); - } - - private function assertAggregateConnectionMapOfCommandAddBuilding( - AggregateConnection $aggregateConnection, - string $aggregateId, - bool $withEvent = false - ): void { - $this->assertAggregateBuilding($aggregateConnection->aggregate(), $aggregateId); - - $commandMap = $aggregateConnection->commandMap(); - $this->assertCount(1, $commandMap); - $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - - $eventMap = $aggregateConnection->eventMap(); - - if ($withEvent === false) { - $this->assertCount(0, $eventMap); - } else { - $this->assertCount(1, $eventMap); - $this->assertEventBuildingAdded($eventMap->current()); - } - } - - private function assertAggregateConnectionMapOfCommandCheckInUser(AggregateConnection $aggregateConnection, string $aggregateId): void - { - $this->assertAggregateBuilding($aggregateConnection->aggregate(), $aggregateId); - - $commandMap = $aggregateConnection->commandMap(); - $this->assertCount(1, $commandMap); - $this->assertCommandCheckInUser($commandMap->current()); - - $eventMap = $aggregateConnection->eventMap(); - $eventMap->rewind(); - $this->assertCount(2, $eventMap); - $this->assertEventUserCheckedIn($eventMap->current()); - - $eventMap->next(); - $this->assertEventDoubleCheckInDetected($eventMap->current()); - } - - private function assertAggregateConnectionMapOfCommandCheckOutUser(AggregateConnection $aggregateConnection): void - { - $this->assertAggregateBuilding($aggregateConnection->aggregate(), 'jKrpwfkdZnT5xMRKMYrgTF'); - - $commandMap = $aggregateConnection->commandMap(); - $this->assertCount(1, $commandMap); - $this->assertCommandCheckOutUser($commandMap->current()); - - $eventMap = $aggregateConnection->eventMap(); - $eventMap->rewind(); - $this->assertCount(1, $eventMap); - $this->assertEventUserCheckedOut($eventMap->current()); - } + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(2, $analyzer->commandMap()); + $this->assertCount(2, $analyzer->aggregateMap()); + $this->assertCount(2, $analyzer->eventMap()); + $this->assertCount(0, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertAggregateBuilding($identityConnection->identity(), self::ID_CHECK_IN_USER_AGGREGATE); + $this->assertAnalysisAddBuilding($analyzer, false); + $this->assertAnalysisBuildingUser($analyzer); - /** - * @test - */ - public function it_returns_aggregate_connection_map_of_event_node(): void - { $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(2, $analyzer->commandMap()); + $this->assertCount(2, $analyzer->aggregateMap()); + $this->assertCount(3, $analyzer->eventMap()); + $this->assertCount(1, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertEventBuildingAdded($identityConnection->identity()); + $this->assertAnalysisAddBuilding($analyzer, true); + $this->assertAnalysisBuildingUser($analyzer); + $this->assertAnalysisBuildingAdded($analyzer); - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); - - $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - - $this->assertCount(1, $aggregateMap); - $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); - - $eventMap = $aggregate->eventMap(); - $this->assertCount(1, $eventMap); - $this->assertEventBuildingAdded($eventMap->current()); + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(2, $analyzer->commandMap()); + $this->assertCount(2, $analyzer->aggregateMap()); + $this->assertCount(3, $analyzer->eventMap()); + $this->assertCount(1, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertAggregateBuilding($identityConnection->identity(), self::ID_ADD_BUILDING_AGGREGATE); + $this->assertAnalysisAddBuilding($analyzer, true); + $this->assertAnalysisBuildingUser($analyzer); + $this->assertAnalysisBuildingAdded($analyzer); + $this->assertAnalysisBuilding($analyzer); - $commandMap = $aggregate->commandMap(); - $this->assertCount(0, $commandMap); + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(3, $analyzer->commandMap()); + $this->assertCount(3, $analyzer->aggregateMap()); + $this->assertCount(4, $analyzer->eventMap()); + $this->assertCount(3, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertFeature($identityConnection->identity()); + $this->assertAnalysisAddBuilding($analyzer, true); + $this->assertAnalysisBuildingUser($analyzer); + $this->assertAnalysisBuildingAdded($analyzer); + $this->assertAnalysisBuilding($analyzer); + $this->assertAnalyzeFeatureBuilding($analyzer); } - /** - * @test - */ - public function it_returns_aggregate_connection_map_of_document_node(): void - { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'name_vo.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); - - $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + private function assertAnalysisAddBuilding( + EventSourcingAnalyzer $analyzer, + bool $updated + ): void { + $commandMap = $analyzer->commandMap(); + $aggregateMap = $analyzer->aggregateMap(); + $featureMap = $analyzer->featureMap(); - $this->assertCount(1, $aggregateMap); + $command = $commandMap->current(); $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'buTwEKKNLBBo6WAERYN1Gn'); - - $documentMap = $aggregate->documentMap(); - $this->assertCount(1, $documentMap); - $this->assertDocumentName($documentMap->current()); - - $commandMap = $aggregate->commandMap(); - $this->assertCount(0, $commandMap); - - $eventMap = $aggregate->eventMap(); - $this->assertCount(0, $eventMap); - } - - /** - * @test - */ - public function it_returns_feature_connection_map_of_event_node(): void - { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); + $feature = $featureMap->current(); - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $this->assertCount(0, $command->from()); + $this->assertCount(1, $command->to()); + $this->assertCount(1, $aggregate->from()); + $this->assertCount($updated ? 1 : 0, $aggregate->to()); - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); - - $this->assertCount(1, $featureConnectionMap); - $featureConnection = $featureConnectionMap->current(); + // commands + $this->assertCommandAddBuilding($command->identity(), self::ID_ADD_BUILDING); - $aggregateMap = $featureConnection->aggregateMap(); - $this->assertCount(1, $aggregateMap); - $this->assertAggregateBuilding($aggregateMap->current(), 'buTwEKKNLBBo6WAERYN1Gn'); + // aggregates + $this->assertAggregateBuilding($aggregate->identity(), self::ID_ADD_BUILDING_AGGREGATE); - $eventMap = $featureConnection->eventMap(); - $this->assertCount(1, $eventMap); - $this->assertEventBuildingAdded($eventMap->current()); + // aggregate connections + $this->assertIdenticalVertex($aggregate->identity(), $command->to()->current()); + $this->assertIdenticalVertex($command->identity(), $aggregate->from()->current()); - $commandMap = $featureConnection->commandMap(); - $this->assertCount(0, $commandMap); + // feature connections + $this->assertIdenticalVertex($aggregate->parent(), $feature->identity()); + $this->assertIdenticalVertex($command->parent(), $feature->identity()); + $this->assertIdenticalVertex($feature->children()->filterByType(VertexType::TYPE_COMMAND)->current(), $command->identity()); + $this->assertIdenticalVertex($feature->children()->filterByType(VertexType::TYPE_AGGREGATE)->current(), $aggregate->identity()); } - /** - * @test - */ - public function it_returns_command_map_of_aggregate_node(): void + private function assertAnalysisBuildingUser(EventSourcingAnalyzer $analyzer): void { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $commandMap = $analyzer->commandMap(); + $aggregateMap = $analyzer->aggregateMap(); + $eventMap = $analyzer->eventMap(); + $featureMap = $analyzer->featureMap(); - $commandMap = $eventSourcingAnalyzer->commandMap(); + $commandMap->next(); + $aggregateMap->next(); - $this->assertCount(1, $commandMap); + // commands $command = $commandMap->current(); + $command->from()->rewind(); + $command->to()->rewind(); - $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); - } + $this->assertCommandCheckInUser($command->identity()); - /** - * @test - */ - public function it_returns_event_map_of_aggregate_node(): void - { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); + // aggregates + $aggregate = $aggregateMap->current(); + $aggregate->from()->rewind(); + $aggregate->to()->rewind(); - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $this->assertAggregateBuilding($aggregate->identity(), self::ID_CHECK_IN_USER_AGGREGATE); - $eventMap = $eventSourcingAnalyzer->eventMap(); + // events + $eventUserCheckedIn = $eventMap->current(); + $eventUserCheckedIn->from()->rewind(); + $eventUserCheckedIn->to()->rewind(); - $this->assertCount(2, $eventMap); - $event = $eventMap->current(); - $this->assertEventUserCheckedIn($event); + $this->assertEventUserCheckedIn($eventUserCheckedIn->identity()); $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventDoubleCheckInDetected($event); - } - - /** - * @test - */ - public function it_returns_aggregate_connection_map_of_aggregate_node(): void - { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); + $eventDoubleCheckInDetected = $eventMap->current(); + $eventDoubleCheckInDetected->from()->rewind(); + $eventDoubleCheckInDetected->to()->rewind(); - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $this->assertEventDoubleCheckInDetected($eventDoubleCheckInDetected->identity()); - $aggregateMap = $eventSourcingAnalyzer->aggregateConnectionMap(); + // aggregate connections + $this->assertIdenticalVertex($aggregate->identity(), $command->to()->current()); + $this->assertIdenticalVertex($command->identity(), $aggregate->from()->current()); - $this->assertCount(1, $aggregateMap); - $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate->aggregate(), 'eiaS8gtsBemMReTNbeNRXj'); + $this->assertIdenticalVertex($aggregate->identity(), $eventUserCheckedIn->from()->current()); + $this->assertIdenticalVertex($aggregate->identity(), $eventDoubleCheckInDetected->from()->current()); - $commandMap = $aggregate->commandMap(); - $this->assertCount(1, $commandMap); - $command = $commandMap->current(); - $this->assertCommandCheckInUser($command); + $this->assertIdenticalVertex($eventUserCheckedIn->identity(), $aggregate->to()->current()); - $eventMap = $aggregate->eventMap(); - $this->assertCount(2, $eventMap); - $event = $eventMap->current(); - $this->assertEventUserCheckedIn($event); + $aggregate->to()->next(); + $this->assertIdenticalVertex($eventDoubleCheckInDetected->identity(), $aggregate->to()->current()); - $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventDoubleCheckInDetected($event); - - $commandsToEventsMap = $aggregate->commandsToEventsMap(); + // feature connections + $feature = $featureMap->current(); + $childrenCommand = $feature->children()->filterByType(VertexType::TYPE_COMMAND); + $childrenCommand->next(); - $this->assertCount(1, $commandsToEventsMap); - $this->assertTrue($commandsToEventsMap->offsetExists($command)); + $childrenAggregate = $feature->children()->filterByType(VertexType::TYPE_AGGREGATE); + $childrenAggregate->next(); - $events = $commandsToEventsMap->offsetGet($command); - $this->assertCount(2, $events); + $this->assertIdenticalVertex($aggregate->parent(), $feature->identity()); + $this->assertIdenticalVertex($command->parent(), $feature->identity()); + $this->assertIdenticalVertex($childrenCommand->current(), $command->identity()); + $this->assertIdenticalVertex($childrenAggregate->current(), $aggregate->identity()); } - /** - * @test - */ - public function it_returns_feature_connection_map_of_aggregate_node(): void + private function assertAnalysisBuilding(EventSourcingAnalyzer $analyzer): void { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $command = $analyzer->connection(self::ID_ADD_BUILDING); + $aggregate = $analyzer->connection(self::ID_ADD_BUILDING_AGGREGATE); + $event = $analyzer->connection(self::ID_BUILDING_ADDED); + $feature = $analyzer->connection(self::ID_FEATURE); - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + $this->assertCount(0, $command->from()); + $this->assertCount(1, $command->to()); + $this->assertCount(1, $aggregate->from()); + $this->assertCount(1, $aggregate->to()); + $this->assertCount(1, $event->from()); + $this->assertCount(1, $event->to()); - $this->assertCount(1, $featureConnectionMap); - $featureConnection = $featureConnectionMap->current(); - - $commandMap = $featureConnection->commandMap(); - $this->assertCount(1, $commandMap); - $command = $commandMap->current(); - $this->assertCommandCheckInUser($command); + // event + $this->assertDocumentBuilding($event->to()->current(), '4gYkBjXufnkWMN5ybfBvPq'); - $eventMap = $featureConnection->eventMap(); - $this->assertCount(2, $eventMap); - $event = $eventMap->current(); - $this->assertEventUserCheckedIn($event); + // aggregate connections + $this->assertIdenticalVertex($aggregate->identity(), $command->to()->current()); + $this->assertIdenticalVertex($aggregate->identity(), $event->from()->current()); + $this->assertIdenticalVertex($command->identity(), $aggregate->from()->current()); + $this->assertIdenticalVertex($event->identity(), $aggregate->to()->current()); - $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventDoubleCheckInDetected($event); + // feature connections + $this->assertIdenticalVertex($aggregate->parent(), $feature->identity()); + $this->assertIdenticalVertex($command->parent(), $feature->identity()); + $this->assertIdenticalVertex($event->parent(), $feature->identity()); } - /** - * @test - */ - public function it_returns_feature_connection_map(): void + private function assertAnalysisBuildingAdded(EventSourcingAnalyzer $analyzer): void { - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); - - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - $eventSourcingAnalyzer->analyse($node); + $commandMap = $analyzer->commandMap(); + $aggregateMap = $analyzer->aggregateMap(); + $eventMap = $analyzer->eventMap(); + $featureMap = $analyzer->featureMap(); - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + $commandMap->next(); + $aggregateMap->next(); - $this->assertCount(1, $featureConnectionMap); - $featureConnection = $featureConnectionMap->current(); + // commands + $command = $commandMap->current(); + $command->from()->rewind(); + $command->to()->rewind(); - $aggregateMap = $featureConnection->aggregateMap(); - $this->assertCount(1, $aggregateMap); + $this->assertCommandCheckInUser($command->identity()); + // aggregates $aggregate = $aggregateMap->current(); - $this->assertAggregateBuilding($aggregate, 'jKrpwfkdZnT5xMRKMYrgTF'); - $this->assertEquals($featureConnection->feature(), $featureConnectionMap->featureByAggregate($aggregate)); - - $this->assertFeatureCommandMap($featureConnection->commandMap()); - $this->assertFeatureEventMap($featureConnection->eventMap()); - $this->assertCount(2, $featureConnection->documentMap()); + $aggregate->from()->rewind(); + $aggregate->to()->rewind(); - $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - $this->assertCount(3, $aggregateConnectionMap); + $this->assertAggregateBuilding($aggregate->identity(), self::ID_CHECK_IN_USER_AGGREGATE); - $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertCommandAddBuilding($aggregateConnection->commandMap()->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - $this->assertEventBuildingAdded($aggregateConnection->eventMap()->current()); - - $aggregateConnectionMap->next(); - $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertCommandCheckInUser($aggregateConnection->commandMap()->current()); + // events + $eventUserCheckedIn = $eventMap->current(); + $eventUserCheckedIn->from()->rewind(); + $eventUserCheckedIn->to()->rewind(); - $eventMap = $aggregateConnection->eventMap(); - $this->assertEventUserCheckedIn($eventMap->current()); + $this->assertEventUserCheckedIn($eventUserCheckedIn->identity()); $eventMap->next(); - $this->assertEventDoubleCheckInDetected($eventMap->current()); - } - - /** - * The last node overrides the previous node. This means that a map overrides any previous node of the same type/name - * from the last analysis. That's why the test uses different ids for the same aggregate. - * - * @test - */ - public function it_analysis_nodes(): void - { - $eventSourcingAnalyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); - - // order is important for assertions - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); - $eventSourcingAnalyzer->analyse($node); - - $this->assertCount(1, $eventSourcingAnalyzer->commandMap()); - $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); - $this->assertCount(0, $eventSourcingAnalyzer->eventMap()); - $this->assertCount(1, $eventSourcingAnalyzer->aggregateConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->documentMap()); - $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); - $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); - $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); - $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); - $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', false); - - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_user.json')); - $eventSourcingAnalyzer->analyse($node); - - $this->assertCount(2, $eventSourcingAnalyzer->commandMap()); - $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); - $this->assertCount(2, $eventSourcingAnalyzer->eventMap()); - $this->assertCount(2, $eventSourcingAnalyzer->aggregateConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->documentMap()); - $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); - $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); - $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); - $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); - $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'eiaS8gtsBemMReTNbeNRXj', false); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'eiaS8gtsBemMReTNbeNRXj'); - - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building_added.json')); - $eventSourcingAnalyzer->analyse($node); - $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn'); - $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); - - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'building.json')); - $eventSourcingAnalyzer->analyse($node); - - $this->assertCount(2, $eventSourcingAnalyzer->commandMap()); - $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); - $this->assertCount(3, $eventSourcingAnalyzer->eventMap()); - $this->assertCount(2, $eventSourcingAnalyzer->aggregateConnectionMap()); - $this->assertCount(1, $eventSourcingAnalyzer->documentMap()); - $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); - $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); - $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); - $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); - $this->assertCount(0, $eventSourcingAnalyzer->boundedContextMap()); - $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'buTwEKKNLBBo6WAERYN1Gn'); - $this->assertAnalysisBuilding($eventSourcingAnalyzer, '4gYkBjXufnkWMN5ybfBvPq'); - - $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'feature_building.json')); - $eventSourcingAnalyzer->analyse($node); - - $this->assertCount(3, $eventSourcingAnalyzer->commandMap()); - $this->assertCount(1, $eventSourcingAnalyzer->aggregateMap()); - $this->assertCount(4, $eventSourcingAnalyzer->eventMap()); - $this->assertCount(3, $eventSourcingAnalyzer->aggregateConnectionMap()); - $this->assertCount(2, $eventSourcingAnalyzer->documentMap()); - $this->assertCount(0, $eventSourcingAnalyzer->policyMap()); - $this->assertCount(0, $eventSourcingAnalyzer->uiMap()); - $this->assertCount(0, $eventSourcingAnalyzer->externalSystemMap()); - $this->assertCount(0, $eventSourcingAnalyzer->hotSpotMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureMap()); - $this->assertCount(1, $eventSourcingAnalyzer->featureConnectionMap()); - $this->assertCount(1, $eventSourcingAnalyzer->boundedContextMap()); - $this->assertAnalysisAddBuilding($eventSourcingAnalyzer, 'jKrpwfkdZnT5xMRKMYrgTF', true); - $this->assertAnalysisBuildingUser($eventSourcingAnalyzer, 'jKrpwfkdZnT5xMRKMYrgTF'); - $this->assertAnalysisBuilding($eventSourcingAnalyzer, 'f9iDWVAz8qT4j37oNzoU1p'); - $this->analyzeFeatureBuilding($eventSourcingAnalyzer); - } - - private function assertAnalysisAddBuilding( - EventSourcingAnalyzer $eventSourcingAnalyzer, - string $aggregateId, - bool $aggregateConnectionMapOfCommandAddBuildingWithEvent - ): void { - $commandMap = $eventSourcingAnalyzer->commandMap(); - $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); - $eventMap = $eventSourcingAnalyzer->eventMap(); - $featureMap = $eventSourcingAnalyzer->featureMap(); - $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); + $eventDoubleCheckInDetected = $eventMap->current(); + $eventDoubleCheckInDetected->from()->rewind(); + $eventDoubleCheckInDetected->to()->rewind(); - // commands - $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - - // aggregates - $this->assertAggregateBuilding($aggregateMap->current(), $aggregateId); + $this->assertEventDoubleCheckInDetected($eventDoubleCheckInDetected->identity()); // aggregate connections - $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertIdenticalVertex($aggregateMap->current(), $aggregateConnection->aggregate()); - $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); - $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, $aggregateId, $aggregateConnectionMapOfCommandAddBuildingWithEvent); - - // feature connections - $featureConnection = $featureConnectionMap->current(); - $this->assertIdenticalVertex($featureMap->current(), $featureConnection->feature()); - $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); - $this->assertIdenticalVertex($aggregateMap->current(), $featureConnection->aggregateMap()->current()); - } - - private function assertAnalysisBuildingUser(EventSourcingAnalyzer $eventSourcingAnalyzer, string $aggregateId): void - { - $commandMap = $eventSourcingAnalyzer->commandMap(); - $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - $eventMap = $eventSourcingAnalyzer->eventMap(); - - // commands - $commandMap->next(); - $this->assertCommandCheckInUser($commandMap->current()); + $this->assertIdenticalVertex($aggregate->identity(), $command->to()->current()); + $this->assertIdenticalVertex($command->identity(), $aggregate->from()->current()); - // events - $eventMap->rewind(); - $this->assertEventUserCheckedIn($eventMap->current()); + $this->assertIdenticalVertex($aggregate->identity(), $eventUserCheckedIn->from()->current()); + $this->assertIdenticalVertex($aggregate->identity(), $eventDoubleCheckInDetected->from()->current()); - $eventMap->next(); - $this->assertEventDoubleCheckInDetected($eventMap->current()); + $this->assertIdenticalVertex($eventUserCheckedIn->identity(), $aggregate->to()->current()); - // aggregate connections - $aggregateConnectionMap->next(); - $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertAggregateConnectionMapOfCommandCheckInUser($aggregateConnection, $aggregateId); - $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); - } + $aggregate->to()->next(); + $this->assertIdenticalVertex($eventDoubleCheckInDetected->identity(), $aggregate->to()->current()); - private function assertAnalysisBuilding( - EventSourcingAnalyzer $eventSourcingAnalyzer, - string $id - ): void { - $eventMap = $eventSourcingAnalyzer->eventMap(); - $documentMap = $eventSourcingAnalyzer->documentMap(); + // feature connections + $feature = $featureMap->current(); + $childrenCommand = $feature->children()->filterByType(VertexType::TYPE_COMMAND); + $childrenCommand->next(); - // events - $eventMap->next(); - $this->assertEventBuildingAdded($eventMap->current()); + $childrenAggregate = $feature->children()->filterByType(VertexType::TYPE_AGGREGATE); + $childrenAggregate->next(); - // documents - $documentMap->rewind(); - $this->assertDocumentBuilding($documentMap->current(), $id); + $this->assertIdenticalVertex($aggregate->parent(), $feature->identity()); + $this->assertIdenticalVertex($command->parent(), $feature->identity()); + $this->assertIdenticalVertex($childrenCommand->current(), $command->identity()); + $this->assertIdenticalVertex($childrenAggregate->current(), $aggregate->identity()); } - private function analyzeFeatureBuilding(EventSourcingAnalyzer $eventSourcingAnalyzer): void + private function assertAnalyzeFeatureBuilding(EventSourcingAnalyzer $analyzer): void { - $commandMap = $eventSourcingAnalyzer->commandMap(); - $aggregateConnectionMap = $eventSourcingAnalyzer->aggregateConnectionMap(); - $eventMap = $eventSourcingAnalyzer->eventMap(); - $aggregateMap = $eventSourcingAnalyzer->aggregateMap(); - $documentMap = $eventSourcingAnalyzer->documentMap(); - $featureMap = $eventSourcingAnalyzer->featureMap(); - $featureConnectionMap = $eventSourcingAnalyzer->featureConnectionMap(); - - // commands - $commandMap->next(); - $this->assertCommandCheckOutUser($commandMap->current()); - - // events - $eventMap->next(); - $this->assertEventUserCheckedOut($eventMap->current()); - - // aggregate connections - $aggregateConnectionMap->next(); - $aggregateConnection = $aggregateConnectionMap->current(); - $this->assertAggregateConnectionMapOfCommandCheckOutUser($aggregateConnection); - - $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); - $this->assertIdenticalVertex($eventMap->current(), $aggregateConnection->eventMap()->current()); - - // documents - $documentMap->next(); - $this->assertDocumentUsersInBuilding($documentMap->current(), 't4mMTjg462VRvMW1L6nSGB'); - - // features - $commandMap->rewind(); - $aggregateConnectionMap->rewind(); - $featureConnection = $featureConnectionMap->current(); - $aggregateConnection = $aggregateConnectionMap->current(); - - $this->assertCommandAddBuilding($commandMap->current(), '9bJ5Y7yuBcfWyei7i2ZSDC'); - $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); - $this->assertIdenticalVertex($commandMap->current(), $aggregateConnection->commandMap()->current()); - $this->assertAggregateConnectionMapOfCommandAddBuilding($aggregateConnection, 'jKrpwfkdZnT5xMRKMYrgTF', true); - - $commandMap->next(); - $featureConnection->commandMap()->next(); - $this->assertCommandCheckInUser($commandMap->current()); - $this->assertIdenticalVertex($commandMap->current(), $featureConnection->commandMap()->current()); - - foreach ($featureConnectionMap as $featureConnection) { - $this->assertIdenticalVertex( - $featureMap->vertex($featureConnection->feature()->name()), - $featureConnection->feature() - ); - foreach ($featureConnection->commandMap() as $command) { - $this->assertIdenticalVertex($commandMap->vertex($command->name()), $command); - } - foreach ($featureConnection->eventMap() as $event) { - $this->assertIdenticalVertex($eventMap->vertex($event->name()), $event); - } - foreach ($featureConnection->aggregateMap() as $aggregate) { - $this->assertIdenticalVertex($aggregateMap->vertex($aggregate->name()), $aggregate); - } - foreach ($featureConnection->documentMap() as $document) { - $this->assertIdenticalVertex($documentMap->vertex($document->name()), $document); - } + $commandMap = $analyzer->commandMap(); + $aggregateMap = $analyzer->aggregateMap(); + $eventMap = $analyzer->eventMap(); + $documentMap = $analyzer->documentMap(); + $featureMap = $analyzer->featureMap(); + + $feature = $featureMap->current(); + + foreach ($feature->children()->filterByType(VertexType::TYPE_COMMAND) as $child) { + $command = $commandMap->current(); + $this->assertIdenticalVertex($child, $command->identity()); + $this->assertSame($feature->identity(), $command->parent()); + $commandMap->next(); } - - foreach ($aggregateConnectionMap as $aggregateConnection) { - $this->assertIdenticalVertex( - $aggregateMap->vertex($aggregateConnection->aggregate()->name()), - $aggregateConnection->aggregate() - ); - - foreach ($aggregateConnection->commandMap() as $command) { - $this->assertIdenticalVertex($commandMap->vertex($command->name()), $command); - } - foreach ($aggregateConnection->eventMap() as $event) { - $this->assertIdenticalVertex($eventMap->vertex($event->name()), $event); - } + foreach ($feature->children()->filterByType(VertexType::TYPE_AGGREGATE) as $child) { + $aggregate = $aggregateMap->current(); + $this->assertIdenticalVertex($child, $aggregate->identity()); + $this->assertSame($feature->identity(), $aggregate->parent()); + $aggregateMap->next(); + } + foreach ($feature->children()->filterByType(VertexType::TYPE_EVENT) as $child) { + $event = $eventMap->current(); + $this->assertIdenticalVertex($child, $event->identity()); + $this->assertSame($feature->identity(), $event->parent()); + $eventMap->next(); + } + foreach ($feature->children()->filterByType(VertexType::TYPE_DOCUMENT) as $child) { + $document = $documentMap->current(); + $this->assertIdenticalVertex($child, $document->identity()); + $this->assertSame($feature->identity(), $document->parent()); + $documentMap->next(); } } @@ -625,35 +385,12 @@ private function assertIdenticalVertex(VertexType $a, VertexType $b): void $this->assertSame(\spl_object_hash($a), \spl_object_hash($b), 'The objects are not identical'); } - private function assertFeatureCommandMap(VertexMap $commandMap): void - { - $this->assertCount(3, $commandMap); - - $command = $commandMap->current(); - $this->assertCommandAddBuilding($command, '9bJ5Y7yuBcfWyei7i2ZSDC'); - - $commandMap->next(); - $command = $commandMap->current(); - $this->assertCommandCheckInUser($command); - } - - private function assertFeatureEventMap(VertexMap $eventMap): void + private function assertFeature(FeatureType $feature): void { - $this->assertCount(4, $eventMap); - $event = $eventMap->current(); - $this->assertEventBuildingAdded($event); - - $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventUserCheckedIn($event); - - $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventUserCheckedOut($event); - - $eventMap->next(); - $event = $eventMap->current(); - $this->assertEventDoubleCheckInDetected($event); + $this->assertSame(self::ID_FEATURE, $feature->id()); + $this->assertSame('Building', $feature->name()); + $this->assertSame('Building', $feature->label()); + $this->assertSame('feature', $feature->type()); } private function assertAggregateBuilding(AggregateType $aggregate, string $id): void @@ -661,12 +398,14 @@ private function assertAggregateBuilding(AggregateType $aggregate, string $id): $this->assertSame($id, $aggregate->id()); $this->assertSame('Building', $aggregate->name()); $this->assertSame('Building ', $aggregate->label()); + $this->assertSame('aggregate', $aggregate->type()); } private function assertCommandAddBuilding(CommandType $command, string $id): void { $this->assertSame($id, $command->id()); $this->assertSame('Add Building', $command->name()); + $this->assertSame('command', $command->type()); } private function assertEventBuildingAdded(EventType $event): void @@ -674,6 +413,7 @@ private function assertEventBuildingAdded(EventType $event): void $this->assertSame('tF2ZuZCXsdQMhRmRXydfuW', $event->id()); $this->assertSame('Building Added', $event->name()); $this->assertSame('Building Added', $event->label()); + $this->assertSame('event', $event->type()); } private function assertCommandCheckInUser(CommandType $command): void @@ -681,6 +421,7 @@ private function assertCommandCheckInUser(CommandType $command): void $this->assertSame('aKvhibi95v18MKjNjb6tL3', $command->id()); $this->assertSame('Check In User', $command->name()); $this->assertSame('Check In User', \trim($command->label())); + $this->assertSame('command', $command->type()); } private function assertCommandCheckOutUser(CommandType $command): void @@ -688,6 +429,7 @@ private function assertCommandCheckOutUser(CommandType $command): void $this->assertSame('dkNTGinM6VY1Qu8zyGURU1', $command->id()); $this->assertSame('Check Out User', $command->name()); $this->assertSame('Check Out User', \trim($command->label())); + $this->assertSame('commmand', $command->type()); } private function assertEventUserCheckedIn(EventType $event): void @@ -695,6 +437,7 @@ private function assertEventUserCheckedIn(EventType $event): void $this->assertSame('q3thtbbiWsgyRqGadCBLte', $event->id()); $this->assertSame('User Checked In', $event->name()); $this->assertSame('User Checked In', $event->label()); + $this->assertSame('event', $event->type()); } private function assertEventUserCheckedOut(EventType $event): void @@ -702,6 +445,7 @@ private function assertEventUserCheckedOut(EventType $event): void $this->assertSame('5cVD57Gt2HtxPU6zonD5vx', $event->id()); $this->assertSame('User Checked Out', $event->name()); $this->assertSame('User Checked Out', $event->label()); + $this->assertSame('event', $event->type()); } private function assertEventDoubleCheckInDetected(EventType $event): void @@ -709,26 +453,30 @@ private function assertEventDoubleCheckInDetected(EventType $event): void $this->assertSame('8H79vCoLa3Y2RrpVy7ZMYE', $event->id()); $this->assertSame('Double Check In Detected', $event->name()); $this->assertSame('Double Check In Detected ', $event->label()); + $this->assertSame('event', $event->type()); } - private function assertDocumentName(DocumentType $event): void + private function assertDocumentName(DocumentType $document): void { - $this->assertSame('a4HLmzMb2g2MVQWXy4BKN1', $event->id()); - $this->assertSame('Name', $event->name()); - $this->assertSame('Name', $event->label()); + $this->assertSame('a4HLmzMb2g2MVQWXy4BKN1', $document->id()); + $this->assertSame('Name', $document->name()); + $this->assertSame('Name', $document->label()); + $this->assertSame('document', $document->type()); } - private function assertDocumentBuilding(DocumentType $event, string $id): void + private function assertDocumentBuilding(DocumentType $document, string $id): void { - $this->assertSame($id, $event->id()); - $this->assertSame('Building', $event->name()); - $this->assertSame('Building ', $event->label()); + $this->assertSame($id, $document->id()); + $this->assertSame('Building', $document->name()); + $this->assertSame('Building ', $document->label()); + $this->assertSame('document', $document->type()); } - private function assertDocumentUsersInBuilding(DocumentType $event, string $id): void + private function assertDocumentUsersInBuilding(DocumentType $document, string $id): void { - $this->assertSame($id, $event->id()); - $this->assertSame('Users In Building', $event->name()); - $this->assertSame('Users In Building ', $event->label()); + $this->assertSame($id, $document->id()); + $this->assertSame('Users In Building', $document->name()); + $this->assertSame('Users In Building ', $document->label()); + $this->assertSame('document', $document->type()); } } From bc6e70eb4eec2973bd00e63ffecc43b05c11d94e Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 9 Jul 2021 09:50:43 +0200 Subject: [PATCH 08/18] Update prooph/php-cs-fixer-config to 0.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 80ac1cf..9375db6 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "phpstan/phpstan": "^0.12.33", "phpstan/phpstan-strict-rules": "^0.12.4", "phpunit/phpunit": "^9.2.6", - "prooph/php-cs-fixer-config": "^0.3", + "prooph/php-cs-fixer-config": "^0.4", "roave/security-advisories": "dev-master" }, "suggest": { From 9628488755057b9dae95c88cba91867602bc4266 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 9 Jul 2021 14:57:15 +0200 Subject: [PATCH 09/18] Add graph() method --- src/EventSourcingAnalyzer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index d0065a5..11805ee 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -91,6 +91,11 @@ public function hotSpotMap(): VertexConnectionMap return $this->identityConnectionMap->filterByType(VertexType::TYPE_HOT_SPOT); } + public function graph(): VertexConnectionMap + { + return $this->identityConnectionMap; + } + public function has(string $id): bool { return $this->identityConnectionMap->has($id); From 335a63e36aa22ec05751fdfeba9e43461a11cbb2 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Wed, 21 Jul 2021 08:51:57 +0200 Subject: [PATCH 10/18] Add role type support --- src/Role.php | 29 +++++++++++++++++++++++++++++ src/Vertex.php | 6 ++++++ 2 files changed, 35 insertions(+) create mode 100644 src/Role.php diff --git a/src/Role.php b/src/Role.php new file mode 100644 index 0000000..7b42d8a --- /dev/null +++ b/src/Role.php @@ -0,0 +1,29 @@ +metadataInstance; + } +} diff --git a/src/Vertex.php b/src/Vertex.php index 76c02ac..398d3e4 100644 --- a/src/Vertex.php +++ b/src/Vertex.php @@ -72,6 +72,12 @@ public static function fromCodyNode( case VertexType::TYPE_POLICY: $class = Policy::class; break; + case VertexType::TYPE_HOT_SPOT: + $class = HotSpot::class; + break; + case VertexType::TYPE_ROLE: + $class = Role::class; + break; case VertexType::TYPE_UI: $class = Ui::class; break; From e7f6c0e8d9cc5a80243c9b8757ce8023d6b980cf Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Wed, 21 Jul 2021 08:52:20 +0200 Subject: [PATCH 11/18] Skip not supported types --- src/EventSourcingGraph.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index bd1cb98..a45e25c 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -42,7 +42,7 @@ public function analyseConnections( $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); foreach ($node->targets() as $target) { - if ($target->type() === 'edge') { + if (! $this->isTypeSupported($target)) { continue; } $targetIdentity = Vertex::fromCodyNode($target, $this->filterName, $this->metadataFactory); @@ -51,7 +51,7 @@ public function analyseConnections( } foreach ($node->sources() as $source) { - if ($source->type() === 'edge') { + if (! $this->isTypeSupported($source)) { continue; } $sourceIdentity = Vertex::fromCodyNode($source, $this->filterName, $this->metadataFactory); @@ -60,7 +60,7 @@ public function analyseConnections( } foreach ($node->children() as $child) { - if ($child->type() === 'edge') { + if (! $this->isTypeSupported($child)) { continue; } $vertexConnectionMap = $this->addParentConnection( @@ -79,7 +79,7 @@ private function addParent( InspectioGraph\VertexConnectionMap $vertexConnectionMap ): InspectioGraph\VertexConnectionMap { if (($parent = $node->parent()) - && $parent->type() !== 'layer' + && $this->isTypeSupported($parent) ) { $parentIdentity = Vertex::fromCodyNode($parent, $this->filterName, $this->metadataFactory); $vertexConnectionMap = $this->addParentConnection($nodeIdentity, $parentIdentity, $vertexConnectionMap); @@ -97,4 +97,11 @@ private function addParent( return $vertexConnectionMap; } + + private function isTypeSupported(Node $node): bool + { + $type = $node->type(); + + return $type !== 'edge' && $type !== 'image' && $type !== 'layer' && $type !== 'freeText' && $type !== 'text' && $type !== 'icon'; + } } From 1c0e675a629e2ce2cdffe65b5bf220d399b38832 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 23 Jul 2021 09:46:41 +0200 Subject: [PATCH 12/18] Add fromArray method --- src/JsonNode.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/JsonNode.php b/src/JsonNode.php index a3680a3..276d5a0 100644 --- a/src/JsonNode.php +++ b/src/JsonNode.php @@ -31,6 +31,11 @@ public static function fromJson(string $json): Node return new self($data['node']); } + public static function fromArray(array $json): Node + { + return new self($json); + } + private function __construct(array $data) { $this->node = $data; From f311f5a12e378c6530bae12c9ed42610e101748b Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Fri, 23 Jul 2021 14:59:09 +0200 Subject: [PATCH 13/18] Add check for supported nodes --- src/EventSourcingAnalyzer.php | 8 +++++--- src/EventSourcingGraph.php | 4 ++++ src/Vertex.php | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index 11805ee..8c7a105 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -32,13 +32,15 @@ public function __construct(EventSourcingGraph $graph) /** * @param Node $node - * @return InspectioGraph\VertexConnection The vertex with it's connections of provided node + * @return InspectioGraph\VertexConnection|null The vertex with it's connections of provided node or null if not supported */ - public function analyse(Node $node): InspectioGraph\VertexConnection + public function analyse(Node $node): ?InspectioGraph\VertexConnection { $this->identityConnectionMap = $this->graph->analyseConnections($node, $this->identityConnectionMap); - return $this->identityConnectionMap->connection($node->id()); + return $this->identityConnectionMap->has($node->id()) + ? $this->identityConnectionMap->connection($node->id()) + : null; } public function commandMap(): VertexConnectionMap diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index a45e25c..107eb59 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -39,6 +39,10 @@ public function analyseConnections( Node $node, InspectioGraph\VertexConnectionMap $vertexConnectionMap ): InspectioGraph\VertexConnectionMap { + if (! $this->isTypeSupported($node)) { + return $vertexConnectionMap; + } + $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); foreach ($node->targets() as $target) { diff --git a/src/Vertex.php b/src/Vertex.php index 398d3e4..a7895c1 100644 --- a/src/Vertex.php +++ b/src/Vertex.php @@ -72,6 +72,9 @@ public static function fromCodyNode( case VertexType::TYPE_POLICY: $class = Policy::class; break; + case VertexType::TYPE_EXTERNAL_SYSTEM: + $class = ExternalSystem::class; + break; case VertexType::TYPE_HOT_SPOT: $class = HotSpot::class; break; From b710bd65d4f25614f9d0aa5b9f922ed430863f6a Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Mon, 26 Jul 2021 15:33:16 +0200 Subject: [PATCH 14/18] Implement clearGraph() method --- src/EventSourcingAnalyzer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index 8c7a105..bd5529d 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -98,6 +98,11 @@ public function graph(): VertexConnectionMap return $this->identityConnectionMap; } + public function clearGraph(): void + { + $this->identityConnectionMap = VertexConnectionMap::emptyMap(); + } + public function has(string $id): bool { return $this->identityConnectionMap->has($id); From 3671c676e3b2d78b43c5b86a5a8d1054b3320917 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Tue, 27 Jul 2021 09:08:31 +0200 Subject: [PATCH 15/18] Support to remove nodes --- src/EventSourcingAnalyzer.php | 5 ++++ src/EventSourcingGraph.php | 13 ++++++++++ tests/EventSourcingAnalyzerTest.php | 38 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index bd5529d..9ec41c0 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -43,6 +43,11 @@ public function analyse(Node $node): ?InspectioGraph\VertexConnection : null; } + public function remove(Node $node): void + { + $this->identityConnectionMap = $this->graph->removeConnection($node, $this->identityConnectionMap); + } + public function commandMap(): VertexConnectionMap { return $this->identityConnectionMap->filterByType(VertexType::TYPE_COMMAND); diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index 107eb59..49e3ad0 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -77,6 +77,19 @@ public function analyseConnections( return $this->addParent($node, $identity, $vertexConnectionMap); } + public function removeConnection( + Node $node, + InspectioGraph\VertexConnectionMap $vertexConnectionMap + ): InspectioGraph\VertexConnectionMap { + if (! $this->isTypeSupported($node)) { + return $vertexConnectionMap; + } + + $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); + + return $this->removeIdentity($identity, $vertexConnectionMap); + } + private function addParent( Node $node, VertexType $nodeIdentity, diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index 1a7dd55..2e03f2c 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -51,6 +51,44 @@ public function setUp(): void }; } + /** + * @test + */ + public function it_removes_nodes(): void + { + $analyzer = new EventSourcingAnalyzer(new EventSourcingGraph($this->filter)); + + // order is important for assertions + $node = JsonNode::fromJson(\file_get_contents(self::FILES_DIR . 'add_building.json')); + $identityConnection = $analyzer->analyse($node); + + $this->assertCount(1, $analyzer->commandMap()); + $this->assertCount(1, $analyzer->aggregateMap()); + $this->assertCount(0, $analyzer->eventMap()); + $this->assertCount(0, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + $this->assertCommandAddBuilding($identityConnection->identity(), self::ID_ADD_BUILDING); + $this->assertAnalysisAddBuilding($analyzer, false); + + $analyzer->remove($node); + + $this->assertCount(0, $analyzer->commandMap()); + $this->assertCount(1, $analyzer->aggregateMap()); + $this->assertCount(0, $analyzer->eventMap()); + $this->assertCount(0, $analyzer->documentMap()); + $this->assertCount(0, $analyzer->policyMap()); + $this->assertCount(0, $analyzer->uiMap()); + $this->assertCount(0, $analyzer->externalSystemMap()); + $this->assertCount(0, $analyzer->hotSpotMap()); + $this->assertCount(1, $analyzer->featureMap()); + $this->assertCount(1, $analyzer->boundedContextMap()); + } + /** * @test */ From 4957c04a8bb467eeb683351fbc91ca3a5acdebd5 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Wed, 22 Sep 2021 09:12:33 +0200 Subject: [PATCH 16/18] Add support to resolve metadata references --- src/EventSourcingGraph.php | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index 49e3ad0..0b50672 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -42,9 +42,12 @@ public function analyseConnections( if (! $this->isTypeSupported($node)) { return $vertexConnectionMap; } + $resolveMetadataReferences = []; $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); + $resolveMetadataReferences[] = $identity; + foreach ($node->targets() as $target) { if (! $this->isTypeSupported($target)) { continue; @@ -52,6 +55,7 @@ public function analyseConnections( $targetIdentity = Vertex::fromCodyNode($target, $this->filterName, $this->metadataFactory); $vertexConnectionMap = $this->addConnection($identity, $targetIdentity, $vertexConnectionMap); $vertexConnectionMap = $this->addParent($target, $targetIdentity, $vertexConnectionMap); + $resolveMetadataReferences[] = $targetIdentity; } foreach ($node->sources() as $source) { @@ -61,20 +65,35 @@ public function analyseConnections( $sourceIdentity = Vertex::fromCodyNode($source, $this->filterName, $this->metadataFactory); $vertexConnectionMap = $this->addConnection($sourceIdentity, $identity, $vertexConnectionMap); $vertexConnectionMap = $this->addParent($source, $sourceIdentity, $vertexConnectionMap); + $resolveMetadataReferences[] = $sourceIdentity; } foreach ($node->children() as $child) { if (! $this->isTypeSupported($child)) { continue; } + $childIdentity = Vertex::fromCodyNode($child, $this->filterName, $this->metadataFactory); $vertexConnectionMap = $this->addParentConnection( - Vertex::fromCodyNode($child, $this->filterName, $this->metadataFactory), + $childIdentity, $identity, $vertexConnectionMap ); + $resolveMetadataReferences[] = $childIdentity; + } + $vertexConnectionMap = $this->addParent($node, $identity, $vertexConnectionMap); + + if (($parent = $node->parent()) + && $this->isTypeSupported($parent) + && $vertexConnectionMap->has($parent->id()) + ) { + $resolveMetadataReferences[] = $vertexConnectionMap->connection($parent->id())->identity(); + } + + foreach ($resolveMetadataReferences as $resolveMetadataReference) { + $this->resolveReference($resolveMetadataReference, $vertexConnectionMap); } - return $this->addParent($node, $identity, $vertexConnectionMap); + return $vertexConnectionMap; } public function removeConnection( @@ -121,4 +140,13 @@ private function isTypeSupported(Node $node): bool return $type !== 'edge' && $type !== 'image' && $type !== 'layer' && $type !== 'freeText' && $type !== 'text' && $type !== 'icon'; } + + private function resolveReference(VertexType $vertex, InspectioGraph\VertexConnectionMap $vertexConnectionMap): void + { + $metadataInstance = $vertex->metadataInstance(); + + if ($metadataInstance instanceof InspectioGraph\Metadata\ResolvesMetadataReference) { + $metadataInstance->resolveMetadataReferences($vertexConnectionMap, $this->filterName); + } + } } From 6c54ccceb913ca0ad0c4d2d98b86943f60e59fab Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Thu, 7 Oct 2021 12:41:27 +0200 Subject: [PATCH 17/18] Fix adding identity with no connections --- src/EventSourcingGraph.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventSourcingGraph.php b/src/EventSourcingGraph.php index 0b50672..cf8e374 100644 --- a/src/EventSourcingGraph.php +++ b/src/EventSourcingGraph.php @@ -45,7 +45,7 @@ public function analyseConnections( $resolveMetadataReferences = []; $identity = Vertex::fromCodyNode($node, $this->filterName, $this->metadataFactory); - + $vertexConnectionMap = $this->addIdentity($identity, $vertexConnectionMap); $resolveMetadataReferences[] = $identity; foreach ($node->targets() as $target) { From 560691e15dc30837fecd41996028261738396df8 Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Tue, 22 Feb 2022 17:20:14 +0100 Subject: [PATCH 18/18] Update event-engine/php-inspectio-graph to ^0.4.0 and prooph/php-cs-fixer-config to ^0.5.0 --- .php_cs => .php-cs-fixer.php | 6 --- README.md | 63 +++++++++++++++++++---------- composer.json | 7 ++-- src/EventSourcingAnalyzer.php | 6 +++ src/JsonNode.php | 1 + src/Vertex.php | 11 +++++ tests/EventSourcingAnalyzerTest.php | 8 ++++ 7 files changed, 70 insertions(+), 32 deletions(-) rename .php_cs => .php-cs-fixer.php (51%) diff --git a/.php_cs b/.php-cs-fixer.php similarity index 51% rename from .php_cs rename to .php-cs-fixer.php index aa91e55..7752a6b 100644 --- a/.php_cs +++ b/.php-cs-fixer.php @@ -1,11 +1,5 @@ See unit tests in `tests` folder for comprehensive examples and how a Cody JSON looks like. +> See unit tests (`EventSourcingAnalyzerTest`) in `tests` folder for comprehensive examples and how a Cody JSON looks like. The following example gives a quick overview how to retrieve information from the `EventSourcingAnalyzer`. ```php analyse($node); // analyze the Cody JSON node +// call $analyzer->analyse($anotherNode) again with other nodes to build up the graph -// get a list of all commands -$commands = $eventSourcingAnalyzer->commandMap(); +$identity = $connection->identity(); -// get a list of all domain events -$events = $eventSourcingAnalyzer->eventMap(); +if ($identity->type() === VertexType::TYPE_AGGREGATE) { + // get connected commands of connection, in this case the aggregate + $commands = $connection->from()->filterByType(VertexType::TYPE_COMMAND); -// get a list of all aggregates -$aggregates = $eventSourcingAnalyzer->aggregateMap(); + // get connected events of connection, in this case the aggregate + $events = $connection->to()->filterByType(VertexType::TYPE_EVENT); -foreach ($aggregates as $aggregate) { - // returns the aggregate - $aggregate->aggregate(); + // get parent of identity, could be a feature or bounded context + $parent = $connection->parent(); - // returns the corresponding commands for this aggregate - $aggregate->commandMap(); + if ($parent->type() === VertexType::TYPE_FEATURE) { + // get parent connection to get connected vertices + $parentConnection = $analyzer->connection($parent->id()); - // returns the corresponding domain events for this aggregate - $aggregate->eventMap(); - - // returns commands with corresponding domain event(s) (the connection between command -> domain event(s)) - $aggregate->commandsToEventsMap(); + // cycle through all children (vertices e.g. commands, events, etc) of this parent + foreach ($parentConnection->children() as $child) { + if ($child->type() === VertexType::TYPE_COMMAND) { + // ... + } + } + } } + +// search in graph for first document in forwarding mode (a "to" connection) +$documentConnection = $analyzer->graph()->findInGraph( + $identity->id(), + static function (VertexType $vertex): bool { + return $vertex->type() === VertexType::TYPE_DOCUMENT; + }, + VertexConnectionMap::WALK_FORWARD +); ``` diff --git a/composer.json b/composer.json index 9375db6..f526430 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^7.4 || ^8.0", - "event-engine/php-inspectio-graph": "dev-feature/simplify-connections", + "event-engine/php-inspectio-graph": "^0.4.0", "ext-json": "*" }, "require-dev": { @@ -40,11 +40,10 @@ "phpstan/phpstan": "^0.12.33", "phpstan/phpstan-strict-rules": "^0.12.4", "phpunit/phpunit": "^9.2.6", - "prooph/php-cs-fixer-config": "^0.4", - "roave/security-advisories": "dev-master" + "prooph/php-cs-fixer-config": "^0.5.0", + "roave/security-advisories": "dev-latest" }, "suggest": { - "open-code-modeling/php-code-generator": "To use the static factory methods of Transformator classes for Code Generator workflow configuration", "open-code-modeling/php-filter": "For pre-configured filters for proper class / method / property names etc." }, "conflict": { diff --git a/src/EventSourcingAnalyzer.php b/src/EventSourcingAnalyzer.php index 9ec41c0..bca06a9 100644 --- a/src/EventSourcingAnalyzer.php +++ b/src/EventSourcingAnalyzer.php @@ -22,6 +22,7 @@ final class EventSourcingAnalyzer implements InspectioGraph\EventSourcingAnalyze use InspectioGraph\EventSourcingGraph; private VertexConnectionMap $identityConnectionMap; + private EventSourcingGraph $graph; public function __construct(EventSourcingGraph $graph) @@ -98,6 +99,11 @@ public function hotSpotMap(): VertexConnectionMap return $this->identityConnectionMap->filterByType(VertexType::TYPE_HOT_SPOT); } + public function roleMap(): VertexConnectionMap + { + return $this->identityConnectionMap->filterByType(VertexType::TYPE_ROLE); + } + public function graph(): VertexConnectionMap { return $this->identityConnectionMap; diff --git a/src/JsonNode.php b/src/JsonNode.php index 276d5a0..8b63c83 100644 --- a/src/JsonNode.php +++ b/src/JsonNode.php @@ -13,6 +13,7 @@ final class JsonNode implements Node { public const DEFAULT_DEPTH = 512; + private const DEFAULT_OPTIONS = \JSON_BIGINT_AS_STRING | \JSON_THROW_ON_ERROR; /** diff --git a/src/Vertex.php b/src/Vertex.php index a7895c1..31b5ae5 100644 --- a/src/Vertex.php +++ b/src/Vertex.php @@ -59,36 +59,47 @@ public static function fromCodyNode( switch ($type) { case VertexType::TYPE_COMMAND: $class = Command::class; + break; case VertexType::TYPE_AGGREGATE: $class = Aggregate::class; + break; case VertexType::TYPE_EVENT: $class = Event::class; + break; case VertexType::TYPE_DOCUMENT: $class = Document::class; + break; case VertexType::TYPE_POLICY: $class = Policy::class; + break; case VertexType::TYPE_EXTERNAL_SYSTEM: $class = ExternalSystem::class; + break; case VertexType::TYPE_HOT_SPOT: $class = HotSpot::class; + break; case VertexType::TYPE_ROLE: $class = Role::class; + break; case VertexType::TYPE_UI: $class = Ui::class; + break; case VertexType::TYPE_FEATURE: $class = Feature::class; + break; case VertexType::TYPE_BOUNDED_CONTEXT: $class = BoundedContext::class; + break; default: throw new RuntimeException(\sprintf('Given type "%s" is not supported', $type)); diff --git a/tests/EventSourcingAnalyzerTest.php b/tests/EventSourcingAnalyzerTest.php index 2e03f2c..0530a1d 100644 --- a/tests/EventSourcingAnalyzerTest.php +++ b/tests/EventSourcingAnalyzerTest.php @@ -24,19 +24,27 @@ final class EventSourcingAnalyzerTest extends TestCase { private const FILES_DIR = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR; + private const ID_FEATURE = 'stW5qRRPsQbowcqux7M2QX'; private const ID_ADD_BUILDING = '9bJ5Y7yuBcfWyei7i2ZSDC'; + private const ID_ADD_BUILDING_AGGREGATE = 'buTwEKKNLBBo6WAERYN1Gn'; + private const ID_BUILDING_ADDED = 'tF2ZuZCXsdQMhRmRXydfuW'; private const ID_CHECK_IN_USER = 'aKvhibi95v18MKjNjb6tL3'; + private const ID_CHECK_IN_USER_AGGREGATE = 'eiaS8gtsBemMReTNbeNRXj'; + private const ID_USER_CHECKED_IN = 'q3thtbbiWsgyRqGadCBLte'; + private const ID_DOUBLE_CHECKED_IN_DETECTED = '8H79vCoLa3Y2RrpVy7ZMYE'; private const ID_CHECK_OUT_USER = 'aKvhibi95v18MKjNjb6tL3'; + private const ID_CHECK_OUT_USER_AGGREGATE = 'eiaS8gtsBemMReTNbeNRXj'; + private const ID_USER_CHECKED_OUT = 'q3thtbbiWsgyRqGadCBLte'; /**