diff --git a/spec/EricksonReyes/DomainDrivenDesign/Application/CommandBusSpec.php b/spec/EricksonReyes/DomainDrivenDesign/Application/CommandBusSpec.php index ce8efce..a458f90 100644 --- a/spec/EricksonReyes/DomainDrivenDesign/Application/CommandBusSpec.php +++ b/spec/EricksonReyes/DomainDrivenDesign/Application/CommandBusSpec.php @@ -6,6 +6,7 @@ use EricksonReyes\DomainDrivenDesign\Application\Exception\DuplicateCommandHandlerException; use EricksonReyes\DomainDrivenDesign\Application\Exception\MissingHandlerMethodException; use EricksonReyes\DomainDrivenDesign\Application\Exception\UnhandledCommandException; +use EricksonReyes\DomainDrivenDesign\Infrastructure\CommandBus as CommandBusInterface; use PhpSpec\ObjectBehavior; class CommandBusSpec extends ObjectBehavior @@ -26,9 +27,13 @@ public function let() $this->handler = new MockHandler(); } + /** + * + */ public function it_is_initializable() { $this->shouldHaveType(CommandBus::class); + $this->shouldHaveType(CommandBusInterface::class); } public function it_accepts_handlers() diff --git a/spec/EricksonReyes/DomainDrivenDesign/Application/EventBusSpec.php b/spec/EricksonReyes/DomainDrivenDesign/Application/EventBusSpec.php new file mode 100644 index 0000000..e3fc676 --- /dev/null +++ b/spec/EricksonReyes/DomainDrivenDesign/Application/EventBusSpec.php @@ -0,0 +1,90 @@ +shouldHaveType(EventBus::class); + $this->shouldHaveType(EventBusInterface::class); + } + + public function it_registers_handlers(EventHandler $eventHandler) + { + $this->register($eventHandler)->shouldBeNull(); + } + + public function it_stores_handlers(EventHandler $eventHandler) + { + $this->register($eventHandler); + $this->handlers()->shouldHaveCount(1); + } + + public function it_prevents_duplicate_event_handlers(EventHandler $eventHandler) + { + $eventHandler->name()->shouldBeCalled()->willReturn(Factory::create()->word); + $this->register($eventHandler); + $this->shouldThrow(DuplicateEventHandlerException::class)->during('register', [ + $eventHandler + ]); + } + + public function it_dispatches_events( + EventHandler $interestedEventHandler, + EventHandler $eventHandler, + Event $event + ) + { + $interestedEventHandler->isInterestedInThis($event)->shouldBeCalled()->willReturn(true); + $eventHandler->isInterestedInThis($event)->shouldBeCalled()->willReturn(false); + $interestedEventHandler->beNotifiedAbout($event)->shouldBeCalled(); + + $this->register($interestedEventHandler); + $this->register($eventHandler); + $this->dispatch($event)->shouldBeNull(); + } + + public function it_can_have_exception_handlers( + ExceptionHandler $exceptionHandler, + EventHandler $eventHandler, + MockBuggedEventHandler $buggedEventHandler, + EventHandler $anotherEventHandler, + Event $event + ) + { + $this->registerExceptionHandler($exceptionHandler)->shouldBeNull(); + + $this->register($eventHandler); + $this->register($buggedEventHandler); + $this->register($anotherEventHandler); + + $eventHandler->beNotifiedAbout($event)->shouldBeCalled(); + $buggedEventHandler->beNotifiedAbout($event)->shouldBeCalled()->willThrow(RuntimeException::class); + $anotherEventHandler->beNotifiedAbout($event)->shouldBeCalled(); + + $eventHandler->isInterestedInThis($event)->shouldBeCalled()->willReturn(true); + $buggedEventHandler->isInterestedInThis($event)->shouldBeCalled()->willReturn(true); + $anotherEventHandler->isInterestedInThis($event)->shouldBeCalled()->willReturn(true); + + $exceptionHandler->handleThis(Argument::type(RuntimeException::class))->shouldBeCalled(); + + $this->dispatch($event); + } +} diff --git a/spec/EricksonReyes/DomainDrivenDesign/Application/MockBuggedEventHandler.php b/spec/EricksonReyes/DomainDrivenDesign/Application/MockBuggedEventHandler.php new file mode 100644 index 0000000..5ab6b18 --- /dev/null +++ b/spec/EricksonReyes/DomainDrivenDesign/Application/MockBuggedEventHandler.php @@ -0,0 +1,43 @@ +alreadyHandlesThisCommand($commandHandlerInstance, $commandClassName)) { + if ($this->commandIsBeingHandledAlready($commandHandlerInstance, $commandClassName)) { throw new DuplicateCommandHandlerException( "{$handlerInstanceClassName} is already handling {$commandClassName}." ); @@ -81,7 +82,7 @@ private function commands(): array * @param string $commandClassName * @return bool */ - private function alreadyHandlesThisCommand($commandHandlerInstance, string $commandClassName): bool + private function commandIsBeingHandledAlready($commandHandlerInstance, string $commandClassName): bool { return in_array($commandHandlerInstance, $this->commands[$commandClassName], true); } diff --git a/src/EricksonReyes/DomainDrivenDesign/Application/EventBus.php b/src/EricksonReyes/DomainDrivenDesign/Application/EventBus.php new file mode 100644 index 0000000..56ddbd9 --- /dev/null +++ b/src/EricksonReyes/DomainDrivenDesign/Application/EventBus.php @@ -0,0 +1,81 @@ +handlers() as $registeredHandler) { + $registeredHandlerClassName = get_class($registeredHandler); + if ($handler instanceof $registeredHandlerClassName) { + throw new DuplicateEventHandlerException( + $handler->name() . ' is already registered in the event bus.' + ); + } + } + $this->handlers[] = $handler; + } + + /** + * @param ExceptionHandler $exceptionHandler + */ + public function registerExceptionHandler(ExceptionHandler $exceptionHandler): void + { + $this->exceptionHandlers[] = $exceptionHandler; + } + + /** + * @param Event $event + */ + public function dispatch(Event $event): void + { + foreach ($this->handlers() as $handler) { + try { + if ($handler->isInterestedInThis($event)) { + $handler->beNotifiedAbout($event); + } + } catch (Exception $exception) { + foreach ($this->exceptionHandlers() as $exceptionHandler) { + $exceptionHandler->handleThis($exception); + } + } + } + } + + /** + * @return EventHandler[] + */ + public function handlers(): array + { + return $this->handlers; + } + + /** + * @return ExceptionHandler[] + */ + public function exceptionHandlers(): array + { + return $this->exceptionHandlers; + } +} diff --git a/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateCommandHandlerException.php b/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateCommandHandlerException.php index ab21876..24eb41e 100644 --- a/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateCommandHandlerException.php +++ b/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateCommandHandlerException.php @@ -2,12 +2,12 @@ namespace EricksonReyes\DomainDrivenDesign\Application\Exception; -use InvalidArgumentException; +use RuntimeException; /** * Class DuplicateCommandHandlerException * @package EricksonReyes\DomainDrivenDesign\Application\Exception */ -final class DuplicateCommandHandlerException extends InvalidArgumentException +final class DuplicateCommandHandlerException extends RuntimeException { } diff --git a/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateEventHandlerException.php b/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateEventHandlerException.php new file mode 100644 index 0000000..dd5dbf9 --- /dev/null +++ b/src/EricksonReyes/DomainDrivenDesign/Application/Exception/DuplicateEventHandlerException.php @@ -0,0 +1,13 @@ +