Passed
Push — master ( 91198d...95a082 )
by Thorsten
01:49
created

AggregateRootTrait::markClean()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
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 4
    public function getIdentifier(): AggregateIdInterface
41
    {
42 4
        return $this->identifier;
43
    }
44
45 2
    public function getRevision(): AggregateRevision
46
    {
47 2
        return $this->revision;
48
    }
49
50 2
    public function getTrackedEvents(): DomainEventSequenceInterface
51
    {
52 2
        return $this->trackedEvents;
53
    }
54
55 4
    protected function __construct(AggregateIdInterface $aggregateId)
56
    {
57 4
        $this->identifier = $aggregateId;
58 4
        $this->revision = AggregateRevision::makeEmpty();
59 4
        $this->trackedEvents = DomainEventSequence::makeEmpty();
60 4
    }
61
62 1
    protected function reflectThat(DomainEventInterface $eventOccured): AggregateRootInterface
63
    {
64 1
        $this->assertExpectedIdentifier($eventOccured, $this->getIdentifier());
65 1
        $aggRoot = clone $this;
66 1
        $aggRoot->revision = $aggRoot->revision->increment();
67 1
        $eventOccured = $eventOccured->withAggregateRevision($aggRoot->revision);
68 1
        $aggRoot->trackedEvents = $aggRoot->trackedEvents->push($eventOccured);
69 1
        $aggRoot->invokeEventHandler($eventOccured);
70 1
        return $aggRoot;
71
    }
72
73 3
    private function reconstitute(DomainEventInterface $historicalEvent): AggregateRootInterface
74
    {
75 3
        $this->assertExpectedIdentifier($historicalEvent, $this->getIdentifier());
76 2
        $aggRoot = clone $this;
77 2
        $expectedAggregateRevision = $aggRoot->revision->increment();
78 2
        $this->assertExpectedRevision($historicalEvent, $expectedAggregateRevision);
79 1
        $aggRoot->revision = $expectedAggregateRevision;
80 1
        $aggRoot->invokeEventHandler($historicalEvent);
81 1
        return $aggRoot;
82
    }
83
84 2
    private function assertExpectedRevision(DomainEventInterface $event, AggregateRevision $expectedRevision): void
85
    {
86 2
        Assertion::true($expectedRevision->equals($event->getAggregateRevision()), sprintf(
87 2
            'Given event-revision %s does not match expected AR revision at %s',
88 2
            $event->getAggregateRevision(),
89 2
            $expectedRevision
90
        ));
91 1
    }
92
93 4
    private function assertExpectedIdentifier(DomainEventInterface $event, AggregateIdInterface $expectedId): void
94
    {
95 4
        Assertion::true($expectedId->equals($event->getAggregateId()), sprintf(
96 4
            'Given event-identifier %s does not match expected AR identifier at %s',
97 4
            $event->getAggregateId(),
98 4
            $expectedId
99
        ));
100 3
    }
101
102 2
    private function invokeEventHandler(DomainEventInterface $event): void
103
    {
104 2
        $handlerName = preg_replace('/Event$/', '', (new \ReflectionClass($event))->getShortName());
105 2
        $handlerMethod = 'when'.ucfirst($handlerName);
106 2
        $handler = [ $this, $handlerMethod ];
107 2
        if (!is_callable($handler)) {
108
            throw new \Exception(sprintf('Handler "%s" is not callable on '.static::class, $handlerMethod));
109
        }
110 2
        call_user_func($handler, $event);
111 2
    }
112
}
113