diff --git a/src/Domain/EventSourcing/EventSourcingAggregateRoot.php b/src/Domain/EventSourcing/EventSourcingAggregateRoot.php new file mode 100644 index 0000000..b103905 --- /dev/null +++ b/src/Domain/EventSourcing/EventSourcingAggregateRoot.php @@ -0,0 +1,70 @@ +apply($event); + $this->record($event); + $this->notify($event); + } + + /** + * @param DomainEvent $event + */ + protected function notify(DomainEvent $event) + { + DomainEventPublisher::instance()->publish($event); + } + + /** + * @param DomainEvent $event + */ + protected function record(DomainEvent $event) + { + $this->recordedEvents[] = $event; + } + + /** + * @param DomainEvent $event + */ + protected function apply(DomainEvent $event) + { + $parts = explode('\\', get_class($event)); + $apply = 'apply' . end($parts); + + $this->$apply($event); + } + + /** + * @return DomainEvent[] + */ + public function recordedEvents() + { + return $this->recordedEvents; + } + + /** + * @return void + */ + public function clearRecordedEvents() + { + $this->recordedEvents = []; + } +} diff --git a/src/Domain/EventSourcing/EventSourcingStore.php b/src/Domain/EventSourcing/EventSourcingStore.php new file mode 100644 index 0000000..c84e51f --- /dev/null +++ b/src/Domain/EventSourcing/EventSourcingStore.php @@ -0,0 +1,25 @@ +version = $version; + $this->events = $events; + } + + /** + * @return int + */ + public function version() + { + return $this->version; + } + + /** + * @inheritdoc + */ + public function getIterator() + { + return new ArrayIterator($this->events); + } +} diff --git a/tests/Domain/DomainEventPublisherTest.php b/tests/Domain/DomainEventPublisherTest.php index 2ce3940..6a28744 100644 --- a/tests/Domain/DomainEventPublisherTest.php +++ b/tests/Domain/DomainEventPublisherTest.php @@ -65,38 +65,3 @@ private function unsubscribe($id) DomainEventPublisher::instance()->unsubscribe($id); } } - -class SpySubscriber implements DomainEventSubscriber -{ - public $domainEvent; - public $isHandled = false; - private $eventName; - - public function __construct($eventName) - { - $this->eventName = $eventName; - } - - public function isSubscribedTo($aDomainEvent) - { - return $this->eventName === $aDomainEvent->name; - } - - public function handle($aDomainEvent) - { - $this->domainEvent = $aDomainEvent; - $this->isHandled = true; - } -} - -class FakeDomainEvent implements DomainEvent -{ - public $name; - - public function __construct($name) - { - $this->name = $name; - } - - public function occurredOn() {} -} diff --git a/tests/Domain/EventSourcing/EventSourcingAggregateRootTest.php b/tests/Domain/EventSourcing/EventSourcingAggregateRootTest.php new file mode 100644 index 0000000..d566846 --- /dev/null +++ b/tests/Domain/EventSourcing/EventSourcingAggregateRootTest.php @@ -0,0 +1,100 @@ +aggregateRoot = new SpyEventSourcingAggregateRoot(); + $this->subscriber = new SpySubscriber('fake-event'); + + DomainEventPublisher::instance()->subscribe($this->subscriber); + } + + /** + * @test + */ + public function itShouldPublishDomainEvent() + { + $this->aggregateRoot->someBusinessLogic(); + + $this->assertTrue($this->subscriber->isHandled); + } + + /** + * @test + */ + public function itShouldApplyDomainEvent() + { + $this->aggregateRoot->someBusinessLogic(); + + $this->assertTrue($this->aggregateRoot->isApplied()); + } + + /** + * @test + */ + public function itShouldRecordDomainEvent() + { + $this->aggregateRoot->someBusinessLogic(); + + $this->assertEquals( + new FakeDomainEvent('fake-event'), + $this->aggregateRoot->recordedEvents()[0] + ); + } + + /** + * @test + */ + public function itShouldClearRecordedDomainEvent() + { + $this->aggregateRoot->someBusinessLogic(); + $this->aggregateRoot->clearRecordedEvents(); + + $this->assertEmpty($this->aggregateRoot->recordedEvents()); + } +} + +class SpyEventSourcingAggregateRoot +{ + use EventSourcingAggregateRoot; + + /** + * @var bool + */ + private $isApplied = false; + + public function someBusinessLogic() + { + $this->publishThat(new FakeDomainEvent('fake-event')); + } + + protected function applyFakeDomainEvent(FakeDomainEvent $domainEvent) + { + $this->isApplied = true; + } + + /** + * @return boolean + */ + public function isApplied() + { + return $this->isApplied; + } +} diff --git a/tests/Domain/FakeDomainEvent.php b/tests/Domain/FakeDomainEvent.php new file mode 100644 index 0000000..cf3054a --- /dev/null +++ b/tests/Domain/FakeDomainEvent.php @@ -0,0 +1,15 @@ +name = $name; + } + + public function occurredOn() {} +} diff --git a/tests/Domain/SpySubscriber.php b/tests/Domain/SpySubscriber.php new file mode 100644 index 0000000..f762758 --- /dev/null +++ b/tests/Domain/SpySubscriber.php @@ -0,0 +1,26 @@ +eventName = $eventName; + } + + public function isSubscribedTo($aDomainEvent) + { + return $this->eventName === $aDomainEvent->name; + } + + public function handle($aDomainEvent) + { + $this->domainEvent = $aDomainEvent; + $this->isHandled = true; + } +}