Passed
Push — master ( 365767...91198d )
by Thorsten
01:54
created

AggregateRootTrait::reconstitute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
cc 1
eloc 8
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * This file is part of the daikon-cqrs/cqrs 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
declare(strict_types=1);
10
11
namespace Daikon\EventSourcing\Aggregate;
12
13
use Assert\Assertion;
14
use Daikon\EventSourcing\Aggregate\Event\DomainEventInterface;
15
use Daikon\EventSourcing\Aggregate\Event\DomainEventSequence;
16
use Daikon\EventSourcing\Aggregate\Event\DomainEventSequenceInterface;
17
18
trait AggregateRootTrait
19
{
20
    /** @var AggregateIdInterface */
21
    private $identifier;
22
23
    /** @var AggregateRevision */
24
    private $revision;
25
26
    /** @var DomainEventSequenceInterface */
27
    private $trackedEvents;
28
29 3
    public static function reconstituteFromHistory(
30
        AggregateIdInterface $aggregateId,
31
        DomainEventSequenceInterface $history
32
    ): AggregateRootInterface {
33 3
        $aggRoot = new static($aggregateId);
34 3
        foreach ($history as $historicalEvent) {
35 3
            $aggRoot = $aggRoot->reconstitute($historicalEvent);
36
        }
37 1
        return $aggRoot;
38
    }
39
40 5
    public function getIdentifier(): AggregateIdInterface
41
    {
42 5
        return $this->identifier;
43
    }
44
45 2
    public function getRevision(): AggregateRevision
46
    {
47 2
        return $this->revision;
48
    }
49
50 3
    public function getTrackedEvents(): DomainEventSequenceInterface
51
    {
52 3
        return $this->trackedEvents;
53
    }
54
55 1
    public function markClean(): AggregateRootInterface
56
    {
57 1
        $aggRoot = clone $this;
58 1
        $aggRoot->trackedEvents = DomainEventSequence::makeEmpty();
59 1
        return $aggRoot;
60
    }
61
62 5
    protected function __construct(AggregateIdInterface $aggregateId)
63
    {
64 5
        $this->identifier = $aggregateId;
65 5
        $this->revision = AggregateRevision::makeEmpty();
66 5
        $this->trackedEvents = DomainEventSequence::makeEmpty();
67 5
    }
68
69 2
    protected function reflectThat(DomainEventInterface $eventOccured): AggregateRootInterface
70
    {
71 2
        $this->assertExpectedIdentifier($eventOccured, $this->getIdentifier());
72 2
        $aggRoot = clone $this;
73 2
        $aggRoot->revision = $aggRoot->revision->increment();
74 2
        $eventOccured = $eventOccured->withAggregateRevision($aggRoot->revision);
75 2
        $aggRoot->trackedEvents = $aggRoot->trackedEvents->push($eventOccured);
76 2
        $aggRoot->invokeEventHandler($eventOccured);
77 2
        return $aggRoot;
78
    }
79
80 3
    private function reconstitute(DomainEventInterface $historicalEvent): AggregateRootInterface
81
    {
82 3
        $this->assertExpectedIdentifier($historicalEvent, $this->getIdentifier());
83 2
        $aggRoot = clone $this;
84 2
        $expectedAggregateRevision = $aggRoot->revision->increment();
85 2
        $this->assertExpectedRevision($historicalEvent, $expectedAggregateRevision);
86 1
        $aggRoot->revision = $expectedAggregateRevision;
87 1
        $aggRoot->invokeEventHandler($historicalEvent);
88 1
        return $aggRoot;
89
    }
90
91 2
    private function assertExpectedRevision(DomainEventInterface $event, AggregateRevision $expectedRevision): void
92
    {
93 2
        Assertion::true($expectedRevision->equals($event->getAggregateRevision()), sprintf(
94 2
            'Given event-revision %s does not match expected AR revision at %s',
95 2
            $event->getAggregateRevision(),
96 2
            $expectedRevision
97
        ));
98 1
    }
99
100 5
    private function assertExpectedIdentifier(DomainEventInterface $event, AggregateIdInterface $expectedId): void
101
    {
102 5
        Assertion::true($expectedId->equals($event->getAggregateId()), sprintf(
103 5
            'Given event-identifier %s does not match expected AR identifier at %s',
104 5
            $event->getAggregateId(),
105 5
            $expectedId
106
        ));
107 4
    }
108
109 3
    private function invokeEventHandler(DomainEventInterface $event): void
110
    {
111 3
        $handlerName = preg_replace('/Event$/', '', (new \ReflectionClass($event))->getShortName());
112 3
        $handlerMethod = 'when'.ucfirst($handlerName);
113 3
        $handler = [ $this, $handlerMethod ];
114 3
        if (!is_callable($handler)) {
115
            throw new \Exception(sprintf('Handler "%s" is not callable on '.static::class, $handlerMethod));
116
        }
117 3
        call_user_func($handler, $event);
118 3
    }
119
}
120