Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add strict apply method & extract the default apply method #94

Merged
merged 2 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 2 additions & 23 deletions src/Aggregate/AggregateRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@

namespace Patchlevel\EventSourcing\Aggregate;

use function end;
use function explode;
use function get_class;
use function method_exists;

abstract class AggregateRoot
{
/** @var array<AggregateChanged> */
Expand All @@ -23,6 +18,8 @@ final protected function __construct()

abstract public function aggregateRootId(): string;

abstract protected function apply(AggregateChanged $event): void;

protected function record(AggregateChanged $event): void
{
$this->playhead++;
Expand Down Expand Up @@ -61,24 +58,6 @@ public static function createFromEventStream(array $stream): self
return $self;
}

protected function apply(AggregateChanged $event): void
{
$method = $this->findApplyMethod($event);

if (!method_exists($this, $method)) {
return;
}

$this->$method($event);
}

private function findApplyMethod(AggregateChanged $event): string
{
$classParts = explode('\\', get_class($event));

return 'apply' . end($classParts);
}

public function playhead(): int
{
return $this->playhead;
Expand Down
21 changes: 21 additions & 0 deletions src/Aggregate/ApplyMethodNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Aggregate;

use function get_class;
use function sprintf;

class ApplyMethodNotFound extends AggregateException
{
public function __construct(AggregateRoot $aggregate, AggregateChanged $event, string $method)
{
parent::__construct(sprintf(
'Apply method "%s::%s" could not be found for the event "%s"',
get_class($aggregate),
$method,
get_class($event)
));
}
}
34 changes: 34 additions & 0 deletions src/Aggregate/DefaultApplyMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Aggregate;

use function end;
use function explode;
use function get_class;
use function method_exists;

/**
* @psalm-require-extends AggregateRoot
*/
trait DefaultApplyMethod
{
protected function apply(AggregateChanged $event): void
{
$method = $this->findApplyMethod($event);

if (!method_exists($this, $method)) {
return;
}

$this->$method($event);
}

private function findApplyMethod(AggregateChanged $event): string
{
$classParts = explode('\\', get_class($event));

return 'apply' . end($classParts);
}
}
34 changes: 34 additions & 0 deletions src/Aggregate/StrictApplyMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Aggregate;

use function end;
use function explode;
use function get_class;
use function method_exists;

/**
* @psalm-require-extends AggregateRoot
*/
trait StrictApplyMethod
{
protected function apply(AggregateChanged $event): void
{
$method = $this->findApplyMethod($event);

if (!method_exists($this, $method)) {
throw new ApplyMethodNotFound($this, $event, $method);
}

$this->$method($event);
}

private function findApplyMethod(AggregateChanged $event): string
{
$classParts = explode('\\', get_class($event));

return 'apply' . end($classParts);
}
}
3 changes: 3 additions & 0 deletions tests/Integration/BasicImplementation/Aggregate/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

namespace Patchlevel\EventSourcing\Tests\Integration\BasicImplementation\Aggregate;

use Patchlevel\EventSourcing\Aggregate\DefaultApplyMethod;
use Patchlevel\EventSourcing\Aggregate\SnapshotableAggregateRoot;
use Patchlevel\EventSourcing\Tests\Integration\BasicImplementation\Events\ProfileCreated;

final class Profile extends SnapshotableAggregateRoot
{
use DefaultApplyMethod;

private string $id;

public function aggregateRootId(): string
Expand Down
3 changes: 3 additions & 0 deletions tests/Integration/Pipeline/Aggregate/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
namespace Patchlevel\EventSourcing\Tests\Integration\Pipeline\Aggregate;

use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
use Patchlevel\EventSourcing\Aggregate\DefaultApplyMethod;
use Patchlevel\EventSourcing\Tests\Integration\Pipeline\Events\NewVisited;
use Patchlevel\EventSourcing\Tests\Integration\Pipeline\Events\OldVisited;
use Patchlevel\EventSourcing\Tests\Integration\Pipeline\Events\PrivacyAdded;
use Patchlevel\EventSourcing\Tests\Integration\Pipeline\Events\ProfileCreated;

final class Profile extends AggregateRoot
{
use DefaultApplyMethod;

private string $id;
private bool $privacy;
private int $visited;
Expand Down
61 changes: 61 additions & 0 deletions tests/Unit/Aggregate/AggregateRootWithStrictApplyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Unit\Aggregate;

use Patchlevel\EventSourcing\Aggregate\ApplyMethodNotFound;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\Message;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\MessageId;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithStrictApply;
use PHPUnit\Framework\TestCase;

class AggregateRootWithStrictApplyTest extends TestCase
{
public function testApplyMethod(): void
{
$id = ProfileId::fromString('1');
$email = Email::fromString('david.badura@patchlevel.de');

$profile = ProfileWithStrictApply::createProfile($id, $email);

self::assertEquals('1', $profile->aggregateRootId());
self::assertEquals(1, $profile->playhead());
self::assertEquals($id, $profile->id());
self::assertEquals($email, $profile->email());

$events = $profile->releaseEvents();

self::assertCount(1, $events);
$event = $events[0];
self::assertEquals(1, $event->playhead());
}

public function testEventWithoutApplyMethod(): void
{
$this->expectException(ApplyMethodNotFound::class);

$profileId = ProfileId::fromString('1');
$email = Email::fromString('david.badura@patchlevel.de');

$messageId = MessageId::fromString('2');

$profile = ProfileWithStrictApply::createProfile($profileId, $email);

$events = $profile->releaseEvents();

self::assertCount(1, $events);
self::assertEquals(1, $profile->playhead());
$event = $events[0];
self::assertEquals(1, $event->playhead());

$profile->publishMessage(
Message::create(
$messageId,
'foo'
)
);
}
}
3 changes: 3 additions & 0 deletions tests/Unit/Fixture/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
namespace Patchlevel\EventSourcing\Tests\Unit\Fixture;

use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
use Patchlevel\EventSourcing\Aggregate\DefaultApplyMethod;

final class Profile extends AggregateRoot
{
use DefaultApplyMethod;

private ProfileId $id;
private Email $email;
/** @var array<Message> */
Expand Down
3 changes: 3 additions & 0 deletions tests/Unit/Fixture/ProfileWithSnapshot.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

namespace Patchlevel\EventSourcing\Tests\Unit\Fixture;

use Patchlevel\EventSourcing\Aggregate\DefaultApplyMethod;
use Patchlevel\EventSourcing\Aggregate\SnapshotableAggregateRoot;

final class ProfileWithSnapshot extends SnapshotableAggregateRoot
{
use DefaultApplyMethod;

private ProfileId $id;
private Email $email;
/** @var array<Message> */
Expand Down
58 changes: 58 additions & 0 deletions tests/Unit/Fixture/ProfileWithStrictApply.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Unit\Fixture;

use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
use Patchlevel\EventSourcing\Aggregate\StrictApplyMethod;

final class ProfileWithStrictApply extends AggregateRoot
{
use StrictApplyMethod;

private ProfileId $id;
private Email $email;

public function id(): ProfileId
{
return $this->id;
}

public function email(): Email
{
return $this->email;
}

public static function createProfile(ProfileId $id, Email $email): self
{
$self = new self();
$self->record(ProfileCreated::raise($id, $email));

return $self;
}

public function publishMessage(Message $message): void
{
$this->record(MessagePublished::raise(
$this->id,
$message,
));
}

public function visitProfile(ProfileId $profileId): void
{
$this->record(ProfileVisited::raise($this->id, $profileId));
}

protected function applyProfileCreated(ProfileCreated $event): void
{
$this->id = $event->profileId();
$this->email = $event->email();
}

public function aggregateRootId(): string
{
return $this->id->toString();
}
}