Passed
Push — master ( 977f82...f4c7d5 )
by Mr
07:24
created

AggregateRootTrait   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Test Coverage

Coverage 96.15%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 43
c 1
b 0
f 0
dl 0
loc 92
ccs 50
cts 52
cp 0.9615
rs 10
wmc 12

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getRevision() 0 3 1
A reflectThat() 0 9 1
A reconstituteFromHistory() 0 9 2
A getTrackedEvents() 0 3 1
A assertExpectedIdentifier() 0 6 1
A getIdentifier() 0 3 1
A __construct() 0 5 1
A reconstitute() 0 9 1
A assertExpectedRevision() 0 6 1
A invokeEventHandler() 0 11 2
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/event-sourcing project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\EventSourcing\Aggregate;
10
11
use Daikon\EventSourcing\Aggregate\Event\DomainEventInterface;
12
use Daikon\EventSourcing\Aggregate\Event\DomainEventSequence;
13
use Daikon\EventSourcing\Aggregate\Event\DomainEventSequenceInterface;
14
use Daikon\Interop\Assertion;
15
use Daikon\Interop\RuntimeException;
16
use ReflectionClass;
17
18
trait AggregateRootTrait
19
{
20
    private AggregateIdInterface $identifier;
21
22
    private AggregateRevision $revision;
23
24
    private DomainEventSequenceInterface $trackedEvents;
25
26 3
    public static function reconstituteFromHistory(
27
        AggregateIdInterface $aggregateId,
28
        DomainEventSequenceInterface $history
29
    ): self {
30 3
        $aggregateRoot = new static($aggregateId);
31 3
        foreach ($history as $historicalEvent) {
32 3
            $aggregateRoot = $aggregateRoot->reconstitute($historicalEvent);
33
        }
34 1
        return $aggregateRoot;
35
    }
36
37 4
    public function getIdentifier(): AggregateIdInterface
38
    {
39 4
        return $this->identifier;
40
    }
41
42 2
    public function getRevision(): AggregateRevision
43
    {
44 2
        return $this->revision;
45
    }
46
47 2
    public function getTrackedEvents(): DomainEventSequenceInterface
48
    {
49 2
        return $this->trackedEvents;
50
    }
51
52 4
    protected function __construct(AggregateIdInterface $aggregateId)
53
    {
54 4
        $this->identifier = $aggregateId;
55 4
        $this->revision = AggregateRevision::makeEmpty();
56 4
        $this->trackedEvents = DomainEventSequence::makeEmpty();
57 4
    }
58
59 1
    protected function reflectThat(DomainEventInterface $eventOccurred): self
60
    {
61 1
        $this->assertExpectedIdentifier($eventOccurred, $this->getIdentifier());
62 1
        $aggregateRoot = clone $this;
63 1
        $aggregateRoot->revision = $aggregateRoot->revision->increment();
64 1
        $eventOccurred = $eventOccurred->withAggregateRevision($aggregateRoot->revision);
65 1
        $aggregateRoot->trackedEvents = $aggregateRoot->trackedEvents->push($eventOccurred);
66 1
        $aggregateRoot->invokeEventHandler($eventOccurred);
67 1
        return $aggregateRoot;
68
    }
69
70 3
    private function reconstitute(DomainEventInterface $historicalEvent): self
71
    {
72 3
        $this->assertExpectedIdentifier($historicalEvent, $this->getIdentifier());
73 2
        $aggregateRoot = clone $this;
74 2
        $expectedAggregateRevision = $aggregateRoot->revision->increment();
75 2
        $this->assertExpectedRevision($historicalEvent, $expectedAggregateRevision);
76 1
        $aggregateRoot->revision = $expectedAggregateRevision;
77 1
        $aggregateRoot->invokeEventHandler($historicalEvent);
78 1
        return $aggregateRoot;
79
    }
80
81 2
    private function assertExpectedRevision(DomainEventInterface $event, AggregateRevision $expectedRevision): void
82
    {
83 2
        Assertion::true($expectedRevision->equals($event->getAggregateRevision()), sprintf(
84 2
            'Given event revision %s does not match expected AR revision at %s.',
85 2
            (string)$event->getAggregateRevision(),
86 2
            (string)$expectedRevision
87
        ));
88 1
    }
89
90 4
    private function assertExpectedIdentifier(DomainEventInterface $event, AggregateIdInterface $expectedId): void
91
    {
92 4
        Assertion::true($expectedId->equals($event->getAggregateId()), sprintf(
93 4
            'Given event identifier %s does not match expected AR identifier at %s.',
94 4
            (string)$event->getAggregateId(),
95 4
            (string)$expectedId
96
        ));
97 3
    }
98
99 2
    private function invokeEventHandler(DomainEventInterface $event): void
100
    {
101 2
        $handlerName = preg_replace('/Event$/', '', (new ReflectionClass($event))->getShortName());
102 2
        $handlerMethod = 'when'.ucfirst($handlerName);
103 2
        $handler = [$this, $handlerMethod];
104 2
        if (!is_callable($handler)) {
105
            throw new RuntimeException(
106
                sprintf("Handler '%s' is not callable on '%s'.", $handlerMethod, static::class)
107
            );
108
        }
109 2
        $handler($event);
110 2
    }
111
}
112