-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEventSourcingRootBehavior.php
More file actions
98 lines (78 loc) · 2.99 KB
/
Copy pathEventSourcingRootBehavior.php
File metadata and controls
98 lines (78 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?php
declare(strict_types=1);
namespace TinyBlocks\BuildingBlocks\Aggregate;
use ReflectionClass;
use ReflectionProperty;
use TinyBlocks\BuildingBlocks\Entity\Identity;
use TinyBlocks\BuildingBlocks\Event\DomainEvent;
use TinyBlocks\BuildingBlocks\Event\EventRecord;
use TinyBlocks\BuildingBlocks\Event\EventRecords;
use TinyBlocks\BuildingBlocks\Event\SequenceNumber;
use TinyBlocks\BuildingBlocks\Internal\Exceptions\EventHandlerMethodNotFound;
use TinyBlocks\BuildingBlocks\Internal\Exceptions\NoEventHandlerRegistered;
use TinyBlocks\BuildingBlocks\Snapshot\Snapshot;
trait EventSourcingRootBehavior
{
use AggregateRootBehavior;
public static function blank(Identity $identity): static
{
$aggregate = new ReflectionClass(static::class)->newInstanceWithoutConstructor();
new ReflectionProperty($aggregate, $aggregate->identityName())
->setValue($aggregate, $identity);
$aggregate->sequenceNumber = SequenceNumber::initial();
$aggregate->recordedEvents = EventRecords::createFromEmpty();
return $aggregate;
}
public static function reconstitute(
Identity $identity,
iterable $records,
?Snapshot $snapshot = null
): static {
$aggregate = static::blank(identity: $identity);
if (!is_null($snapshot)) {
$aggregate->applySnapshot(snapshot: $snapshot);
$aggregate->sequenceNumber = $snapshot->sequenceNumber();
}
foreach ($records as $record) {
$aggregate->applyEvent(record: $record);
}
return $aggregate;
}
public function eventHandlers(): array
{
return [];
}
public function snapshotState(): array
{
$state = get_object_vars($this);
unset($state['recordedEvents'], $state['sequenceNumber']);
return $state;
}
protected function when(DomainEvent $event): void
{
$this->nextSequenceNumber();
$record = $this->buildEventRecord(event: $event);
$this->applyEvent(record: $record);
$this->recordedEvents = ($this->recordedEvents ?? EventRecords::createFromEmpty())
->add(elements: $record);
}
protected function applyEvent(EventRecord $record): void
{
$handlers = $this->eventHandlers();
$eventClass = $record->event::class;
if ($handlers !== []) {
if (!array_key_exists($eventClass, $handlers)) {
throw new NoEventHandlerRegistered(eventClass: $eventClass, aggregateClass: static::class);
}
$handlers[$eventClass]($record->event);
$this->sequenceNumber = $record->sequenceNumber;
return;
}
$methodName = sprintf('when%s', $record->type->value);
if (!method_exists($this, $methodName)) {
throw new EventHandlerMethodNotFound(methodName: $methodName, aggregateClass: static::class);
}
$this->{$methodName}($record->event);
$this->sequenceNumber = $record->sequenceNumber;
}
}