Skip to content

Commit

Permalink
Merge pull request #406 from sonja-turo/main
Browse files Browse the repository at this point in the history
Fix snapshots in aggregate partials
  • Loading branch information
sebastiandedeyne authored Oct 2, 2023
2 parents 49e5657 + f70ffdf commit b891874
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 2 deletions.
20 changes: 20 additions & 0 deletions src/AggregateRoots/AggregatePartial.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Spatie\EventSourcing\AggregateRoots;

use Ramsey\Uuid\Uuid;
use ReflectionClass;
use ReflectionProperty;
use Spatie\EventSourcing\EventHandlers\AppliesEvents;
use Spatie\EventSourcing\StoredEvents\ShouldBeStored;
use Spatie\EventSourcing\StoredEvents\StoredEvent;
Expand Down Expand Up @@ -35,6 +37,24 @@ public function apply(StoredEvent | ShouldBeStored ...$storedEvents): void
}
}

public function getState(): array
{
$class = new ReflectionClass($this);

return collect($class->getProperties(ReflectionProperty::IS_PUBLIC))
->reject(fn (ReflectionProperty $reflectionProperty) => $reflectionProperty->isStatic())
->mapWithKeys(function (ReflectionProperty $property) {
return [$property->getName() => $this->{$property->getName()}];
})->toArray();
}

public function useState(array $state): void
{
foreach ($state as $key => $value) {
$this->$key = $value;
}
}

public static function fake(): static
{
$aggregateRoot = FakeAggregateRootForPartial::retrieve(Uuid::uuid4()->toString());
Expand Down
28 changes: 26 additions & 2 deletions src/AggregateRoots/AggregateRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,37 @@ protected function getState(): array
->reject(fn (ReflectionProperty $reflectionProperty) => $reflectionProperty->isStatic())
->mapWithKeys(function (ReflectionProperty $property) {
return [$property->getName() => $this->{$property->getName()}];
})->toArray();
})
->merge($this->getPartialsState())
->toArray();
}

protected function getPartialsState(): array
{
$partials = [];
foreach ($this->resolvePartials() as $partial) {
$partials[$partial::class] = $partial->getState();
}
return $partials;
}

protected function restorePartialState(string $key, array $state): void
{
foreach ($this->resolvePartials() as $partial) {
if ($partial::class === $key) {
$partial->useState($state);
}
}
}

protected function useState(array $state): void
{
foreach ($state as $key => $value) {
$this->$key = $value;
if (class_exists($key)) {
$this->$key = $this->restorePartialState($key, $value);
} else {
$this->$key = $value;
}
}
}

Expand Down
38 changes: 38 additions & 0 deletions tests/AggregateRootTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use function PHPUnit\Framework\assertEmpty;
use function PHPUnit\Framework\assertEquals;
use function PHPUnit\Framework\assertInstanceOf;
use function PHPUnit\Framework\assertNotEmpty;
use function PHPUnit\Framework\assertTrue;

use Spatie\EventSourcing\AggregateRoots\AggregateRoot;
Expand All @@ -22,9 +23,11 @@
use Spatie\EventSourcing\StoredEvents\Models\EloquentStoredEvent;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRoot;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRootWithFailingPersist;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRootWithPartial;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRootWithStoredEventRepositorySpecified;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Mailable\MoneyAddedMailable;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Math;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Partials\MoneyPartial;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Projectors\AccountProjector;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Reactors\DoubleBalanceReactor;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Reactors\SendMailReactor;
Expand Down Expand Up @@ -168,6 +171,22 @@
});
});

it('should store partial states when snapshotting', function () {
/** @var \Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRoot $aggregateRoot */
$aggregateRoot = AccountAggregateRootWithPartial::retrieve($this->aggregateUuid);

$aggregateRoot
->addMoney(100)
->addMoney(100)
->addMoney(100);

$aggregateRoot->snapshot();

tap(EloquentSnapshot::first(), function (EloquentSnapshot $snapshot) {
assertEquals(300, $snapshot->state[MoneyPartial::class]['balance']);
});
});

it('should restore public properties when restoring an aggregate root with a snapshot', function () {
/** @var \Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRoot $aggregateRoot */
$aggregateRoot = AccountAggregateRoot::retrieve($this->aggregateUuid);
Expand Down Expand Up @@ -204,6 +223,25 @@
assertEquals(400, $aggregateRootRetrieved->balance);
});

it('should have partials reconstituted if present', function () {
/** @var \Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRootWithPartial $aggregateRoot */
$aggregateRoot = AccountAggregateRootWithPartial::retrieve($this->aggregateUuid);

$aggregateRoot
->addMoney(100)
->addMoney(100)
->addMoney(100)
->persist();

$aggregateRoot->snapshot();
$aggregateRoot->addMoney(100)->persist();

$aggregateRootRetrieved = AccountAggregateRootWithPartial::retrieve($this->aggregateUuid);

assertEquals(4, $aggregateRootRetrieved->aggregateVersion);
assertEquals(400, $aggregateRootRetrieved->getBalance());
});

it('should replay all events in the correct order when retrieving an aggregate root all', function () {
/** @var \Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\AccountAggregateRoot $aggregateRoot */
$aggregateRoot = AccountAggregateRoot::retrieve($this->aggregateUuid);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Spatie\EventSourcing\Tests\TestClasses\AggregateRoots;

use Spatie\EventSourcing\AggregateRoots\AggregateRoot;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Partials\MoneyPartial;

class AccountAggregateRootWithPartial extends AggregateRoot
{
public int $aggregateVersion = 0;

public int $aggregateVersionAfterReconstitution = 0;

public $dependency;

protected Math $math;

protected MoneyPartial $moneyPartial;

public function __construct(Math $math, $dependency = null)
{
$this->dependency = $dependency;
$this->math = $math;

$this->moneyPartial = new MoneyPartial($this, $math);
}

public function addMoney(int $amount): self
{
$this->moneyPartial->addMoney($amount);

return $this;
}

public function multiplyMoney(int $amount): self
{
$this->moneyPartial->multiplyMoney($amount);

return $this;
}

public function getBalance()
{
return $this->moneyPartial->balance;
}
}
47 changes: 47 additions & 0 deletions tests/TestClasses/AggregateRoots/Partials/MoneyPartial.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Partials;

use Spatie\EventSourcing\AggregateRoots\AggregatePartial;
use Spatie\EventSourcing\AggregateRoots\AggregateRoot;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\Math;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\StorableEvents\MoneyAdded;
use Spatie\EventSourcing\Tests\TestClasses\AggregateRoots\StorableEvents\MoneyMultiplied;

class MoneyPartial extends AggregatePartial
{
public int $balance = 0;

protected Math $math;

public function __construct(AggregateRoot $aggregateRoot, Math $math)
{
parent::__construct($aggregateRoot);
$this->math = $math;
}

public function addMoney(int $amount): self
{
$this->recordThat(new MoneyAdded($amount));

return $this;
}

public function multiplyMoney(int $amount): self
{
$this->recordThat(new MoneyMultiplied($amount));

return $this;
}

protected function applyMoneyAdded(MoneyAdded $event)
{
$this->balance += $event->amount;
}

public function applyMoneyMultiplied(MoneyMultiplied $event)
{
$this->balance = $this->math->multiply($this->balance, $event->amount);
}

}

0 comments on commit b891874

Please sign in to comment.