From a9ca03a9d882cf03d1e8ca48f17f9780a62ec1f5 Mon Sep 17 00:00:00 2001 From: Ian Jenkins Date: Mon, 19 Jan 2015 17:54:36 +0000 Subject: [PATCH] Version25 Adding command bus and separate out command handling from domain. I opted for SimpleBus because I liked the simplicity and have a lot of respect for it's maintainer Mathias. I did have some misgivings over the use of a type hint for commands, but generally it suited what I needed to do well, it also forced me to create an explicit CommandHandling layer, which I think works quite well. It's been completely changed in version 2 now anyway, and is more of a generic 'MessageBus' now. There are quite a few command buses that have popped up recently, I created CommandBusCommandBus as a case in point. Anyway the point is I wanted to introduce a command bus to make my example complete. Here you can see how I've moved all the command handling bits to the CommandHandling later I spoke about above. It just means that rather than passing command to handlers directly, I can just 'throw' them on to the command bus now. This has the advantages of being able to decorate the command bus to be able to perform additional tasks against commands should I want to; I can make the command bus handle commands asynchronously should I choose; plus other niceities that arise when delegating message handling in this way. --- app/AppKernel.php | 1 + composer.json | 3 +- composer.lock | 313 +++++++++++++++++- .../Handler/EnterRoomHandlerSpec.php | 6 +- .../Handler/ExitRoomHandlerSpec.php | 6 +- src/Jenko/House/Command/EnterRoomCommand.php | 8 - src/Jenko/House/Command/ExitRoomCommand.php | 8 - src/Jenko/House/Handler/HandlerInterface.php | 12 - .../Command/NavigateHouseCommand.php | 14 +- .../Controller/EnterRoomController.php | 21 +- .../Controller/OutsideController.php | 7 + .../HouseBundle/Resources/config/services.yml | 7 +- .../Command/EnterRoomCommand.php | 21 ++ .../Command/ExitRoomCommand.php | 21 ++ .../Handler/EnterRoomHandler.php | 12 +- .../Handler/ExitRoomHandler.php | 10 +- 16 files changed, 406 insertions(+), 64 deletions(-) rename spec/Jenko/{House => HouseCommandHandling}/Handler/EnterRoomHandlerSpec.php (78%) rename spec/Jenko/{House => HouseCommandHandling}/Handler/ExitRoomHandlerSpec.php (79%) delete mode 100644 src/Jenko/House/Command/EnterRoomCommand.php delete mode 100644 src/Jenko/House/Command/ExitRoomCommand.php delete mode 100644 src/Jenko/House/Handler/HandlerInterface.php create mode 100644 src/Jenko/HouseCommandHandling/Command/EnterRoomCommand.php create mode 100644 src/Jenko/HouseCommandHandling/Command/ExitRoomCommand.php rename src/Jenko/{House => HouseCommandHandling}/Handler/EnterRoomHandler.php (76%) rename src/Jenko/{House => HouseCommandHandling}/Handler/ExitRoomHandler.php (75%) diff --git a/app/AppKernel.php b/app/AppKernel.php index e6cb4ae..2d44041 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -16,6 +16,7 @@ public function registerBundles() new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), + new SimpleBus\SymfonyBridge\SimpleBusCommandBusBundle(), // House Bundles new Jenko\HouseBundle\JenkoHouseBundle(), diff --git a/composer.json b/composer.json index 363110d..d7bf45b 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "sensio/distribution-bundle": "~3.0", "sensio/framework-extra-bundle": "~3.0", "incenteev/composer-parameter-handler": "~2.0", - "phpspec/phpspec": "~2.0@RC" + "phpspec/phpspec": "~2.0@RC", + "simple-bus/symfony-bridge": "~1.0" }, "require-dev": { "sensio/generator-bundle": "~2.3", diff --git a/composer.lock b/composer.lock index 359a92e..766fe73 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,57 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "b07b9c817f34ed7159e9080bfc89da49", + "hash": "115e7187dc20919aa5a25cd099d051ed", "packages": [ + { + "name": "beberlei/assert", + "version": "v2.3", + "source": { + "type": "git", + "url": "https://github.com/beberlei/assert.git", + "reference": "160eba4d1fbe692e42b3cf8a20b92ab23e3a8759" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/assert/zipball/160eba4d1fbe692e42b3cf8a20b92ab23e3a8759", + "reference": "160eba4d1fbe692e42b3cf8a20b92ab23e3a8759", + "shasum": "" + }, + "require": { + "ext-mbstring": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Assert": "lib/" + }, + "files": [ + "lib/Assert/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Thin assertion library for input validation in business models.", + "keywords": [ + "assert", + "assertion", + "validation" + ], + "time": "2014-12-18 19:12:40" + }, { "name": "doctrine/annotations", "version": "v1.2.1", @@ -1389,6 +1438,268 @@ "description": "A security checker for your composer.lock", "time": "2014-07-19 10:52:35" }, + { + "name": "simple-bus/command-bus", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/SimpleBus/CommandBus.git", + "reference": "e9a72ac3675743243ce5ab71c9d83f79d728c279" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleBus/CommandBus/zipball/e9a72ac3675743243ce5ab71c9d83f79d728c279", + "reference": "e9a72ac3675743243ce5ab71c9d83f79d728c279", + "shasum": "" + }, + "require": { + "beberlei/assert": "~2.0", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "suggest": { + "simple-bus/command-event-bridge": "Bridge for using both command buses and event buses", + "simple-bus/doctrine-orm-bridge": "Bridge for using command buses and event buses with Doctrine ORM", + "simple-bus/event-bus": "Simple event bus implementation", + "simple-bus/symfony-bridge": "Bridge for using command buses and event buses with Symfony" + }, + "type": "library", + "autoload": { + "psr-4": { + "SimpleBus\\Command\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Noback", + "email": "matthiasnoback@gmail.com", + "homepage": "http://php-and-symfony.matthiasnoback.nl" + } + ], + "description": " Stand-alone command bus library for PHP ", + "homepage": "http://github.com/SimpleBus/CommandBus", + "keywords": [ + "command", + "command bus" + ], + "time": "2014-11-07 11:44:16" + }, + { + "name": "simple-bus/command-event-bridge", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/SimpleBus/CommandEventBridge.git", + "reference": "b2dc0688cad744f1e4b2812f4f059fe5706891f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleBus/CommandEventBridge/zipball/b2dc0688cad744f1e4b2812f4f059fe5706891f4", + "reference": "b2dc0688cad744f1e4b2812f4f059fe5706891f4", + "shasum": "" + }, + "require": { + "beberlei/assert": "~2.0", + "php": ">=5.4", + "simple-bus/command-bus": "~1.0", + "simple-bus/event-bus": "~1.0" + }, + "suggest": { + "simple-bus/doctrine-orm-bridge": "Bridge for using command buses and event buses with Doctrine ORM", + "simple-bus/symfony-bridge": "Bridge for using command buses and event buses with Symfony" + }, + "type": "library", + "autoload": { + "psr-4": { + "SimpleBus\\CommandEventBridge\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Noback", + "email": "matthiasnoback@gmail.com", + "homepage": "http://php-and-symfony.matthiasnoback.nl" + } + ], + "description": " Bridge for combining command buses and event buses in one project ", + "homepage": "http://github.com/SimpleBus/CommandEventBridge", + "keywords": [ + "command", + "command bus", + "event", + "event bus" + ], + "time": "2014-11-07 11:45:45" + }, + { + "name": "simple-bus/doctrine-orm-bridge", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/SimpleBus/DoctrineORMBridge.git", + "reference": "7d956772f5bd30e0143694cc994acd440860e009" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleBus/DoctrineORMBridge/zipball/7d956772f5bd30e0143694cc994acd440860e009", + "reference": "7d956772f5bd30e0143694cc994acd440860e009", + "shasum": "" + }, + "require": { + "beberlei/assert": "~2.0", + "doctrine/orm": "~2.0", + "php": ">=5.4", + "simple-bus/command-bus": "~1.0", + "simple-bus/command-event-bridge": "~1.0", + "simple-bus/event-bus": "~1.0" + }, + "suggest": { + "simple-bus/symfony-bridge": "Bridge for using command buses and event buses with Symfony" + }, + "type": "library", + "autoload": { + "psr-4": { + "SimpleBus\\DoctrineORMBridge\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Noback", + "email": "matthiasnoback@gmail.com", + "homepage": "http://php-and-symfony.matthiasnoback.nl" + } + ], + "description": "Doctrine ORM bridge for using command and event buses", + "homepage": "http://github.com/SimpleBus/DoctrineORMBridge", + "keywords": [ + "command bus", + "doctrine", + "event bus" + ], + "time": "2014-11-07 11:46:21" + }, + { + "name": "simple-bus/event-bus", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/SimpleBus/EventBus.git", + "reference": "016bb9e8fbd8263e5b53ed49a6a64a45e35b6da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleBus/EventBus/zipball/016bb9e8fbd8263e5b53ed49a6a64a45e35b6da0", + "reference": "016bb9e8fbd8263e5b53ed49a6a64a45e35b6da0", + "shasum": "" + }, + "require": { + "beberlei/assert": "~2.0", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "suggest": { + "simple-bus/command-bus": "Simple command bus implementation", + "simple-bus/command-event-bridge": "Bridge for using both command buses and event buses", + "simple-bus/doctrine-orm-bridge": "Bridge for using command buses and event buses with Doctrine ORM", + "simple-bus/symfony-bridge": "Bridge for using command buses and event buses with Symfony" + }, + "type": "library", + "autoload": { + "psr-4": { + "SimpleBus\\Event\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Noback", + "email": "matthiasnoback@gmail.com", + "homepage": "http://php-and-symfony.matthiasnoback.nl" + } + ], + "description": "Stand-alone event bus library for PHP", + "homepage": "http://github.com/SimpleBus/EventBus", + "keywords": [ + "event", + "event bus" + ], + "time": "2014-11-07 11:46:50" + }, + { + "name": "simple-bus/symfony-bridge", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/SimpleBus/SymfonyBridge.git", + "reference": "e54a07ae1149198228de187fae10e1e506ea2028" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleBus/SymfonyBridge/zipball/e54a07ae1149198228de187fae10e1e506ea2028", + "reference": "e54a07ae1149198228de187fae10e1e506ea2028", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "~1.0", + "doctrine/orm": "~2.3", + "php": ">=5.4", + "simple-bus/command-bus": "~1.0", + "simple-bus/command-event-bridge": "~1.0", + "simple-bus/doctrine-orm-bridge": "~1.0", + "simple-bus/event-bus": "~1.0", + "symfony/dependency-injection": "~2.3", + "symfony/http-kernel": "~2.3", + "symfony/yaml": "~2.3" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "SimpleBus\\SymfonyBridge\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Noback", + "email": "matthiasnoback@gmail.com", + "homepage": "http://php-and-symfony.matthiasnoback.nl" + } + ], + "description": " Bridge for using command buses and event buses in Symfony projects ", + "homepage": "http://github.com/SimpleBus/SymfonyBridge", + "keywords": [ + "command bus", + "doctrine", + "event bus", + "symfony" + ], + "time": "2014-11-07 11:15:13" + }, { "name": "swiftmailer/swiftmailer", "version": "v5.3.0", diff --git a/spec/Jenko/House/Handler/EnterRoomHandlerSpec.php b/spec/Jenko/HouseCommandHandling/Handler/EnterRoomHandlerSpec.php similarity index 78% rename from spec/Jenko/House/Handler/EnterRoomHandlerSpec.php rename to spec/Jenko/HouseCommandHandling/Handler/EnterRoomHandlerSpec.php index d49c87b..de5ba4d 100644 --- a/spec/Jenko/House/Handler/EnterRoomHandlerSpec.php +++ b/spec/Jenko/HouseCommandHandling/Handler/EnterRoomHandlerSpec.php @@ -1,10 +1,10 @@ shouldHaveType('Jenko\House\Handler\EnterRoomHandler'); + $this->shouldHaveType('Jenko\HouseCommandHandling\Handler\EnterRoomHandler'); } function it_dispatches_events(EventDispatcherInterface $dispatcher) diff --git a/spec/Jenko/House/Handler/ExitRoomHandlerSpec.php b/spec/Jenko/HouseCommandHandling/Handler/ExitRoomHandlerSpec.php similarity index 79% rename from spec/Jenko/House/Handler/ExitRoomHandlerSpec.php rename to spec/Jenko/HouseCommandHandling/Handler/ExitRoomHandlerSpec.php index a7ec153..0a811bb 100644 --- a/spec/Jenko/House/Handler/ExitRoomHandlerSpec.php +++ b/spec/Jenko/HouseCommandHandling/Handler/ExitRoomHandlerSpec.php @@ -1,8 +1,8 @@ shouldHaveType('Jenko\House\Handler\ExitRoomHandler'); + $this->shouldHaveType('Jenko\HouseCommandHandling\Handler\ExitRoomHandler'); } function it_dispatches_events(EventDispatcherInterface $dispatcher) diff --git a/src/Jenko/House/Command/EnterRoomCommand.php b/src/Jenko/House/Command/EnterRoomCommand.php deleted file mode 100644 index 5fded91..0000000 --- a/src/Jenko/House/Command/EnterRoomCommand.php +++ /dev/null @@ -1,8 +0,0 @@ -enterRoomHandler = $this->getContainer()->get('jenko.house.handlers.enter_room_handler'); + $this->commandBus = $this->getContainer()->get('command_bus'); } /** @@ -59,6 +59,7 @@ protected function initialize(InputInterface $input, OutputInterface $output) */ protected function execute(InputInterface $input, OutputInterface $output) { + $house = $this->house; $room = $input->getOption('location'); if (null === $room) { @@ -66,9 +67,10 @@ protected function execute(InputInterface $input, OutputInterface $output) } $command = new EnterRoomCommand(); + $command->house = $house; $command->room = $room; - $house = $this->enterRoomHandler->handle($command); + $this->commandBus->handle($command); $output->writeln('You are in: ' . $room); diff --git a/src/Jenko/HouseBundle/Controller/EnterRoomController.php b/src/Jenko/HouseBundle/Controller/EnterRoomController.php index a32f851..028df9b 100644 --- a/src/Jenko/HouseBundle/Controller/EnterRoomController.php +++ b/src/Jenko/HouseBundle/Controller/EnterRoomController.php @@ -2,14 +2,15 @@ namespace Jenko\HouseBundle\Controller; -use Jenko\House\Command\EnterRoomCommand; -use Jenko\House\Handler\HandlerInterface; +use Jenko\House\Factory\HomeAloneHouseFactory; use Jenko\House\House; +use Jenko\HouseCommandHandling\Command\EnterRoomCommand; +use SimpleBus\Command\Bus\CommandBus; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -class EnterRoomController +final class EnterRoomController { /** * @var EngineInterface @@ -17,17 +18,18 @@ class EnterRoomController private $templating; /** - * @var HandlerInterface $handler + * @var CommandBus $commandBus */ - private $handler; + private $commandBus; /** * @param EngineInterface $templating + * @param CommandBus $commandBus */ - public function __construct(EngineInterface $templating, HandlerInterface $handler) + public function __construct(EngineInterface $templating, CommandBus $commandBus) { $this->templating = $templating; - $this->handler = $handler; + $this->commandBus = $commandBus; } /** @@ -38,14 +40,15 @@ public function __construct(EngineInterface $templating, HandlerInterface $handl public function enterAction(Request $request) { $command = new EnterRoomCommand(); + $command->house = HomeAloneHouseFactory::getHouse(); $command->room = $request->get('location'); /** @var House $house */ - $house = $this->handler->handle($command); + $this->commandBus->handle($command); return $this->templating->renderResponse( 'JenkoHouseBundle::room.html.twig', - ['currentRoom' => $house->whereAmI(), 'previousRoom' => $house->whereWasI()] + ['currentRoom' => $command->house->whereAmI(), 'previousRoom' => $command->house->whereWasI()] ); } } diff --git a/src/Jenko/HouseBundle/Controller/OutsideController.php b/src/Jenko/HouseBundle/Controller/OutsideController.php index 5d47dee..b72b6ab 100644 --- a/src/Jenko/HouseBundle/Controller/OutsideController.php +++ b/src/Jenko/HouseBundle/Controller/OutsideController.php @@ -3,6 +3,7 @@ namespace Jenko\HouseBundle\Controller; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; +use Symfony\Component\HttpFoundation\Response; class OutsideController { @@ -11,11 +12,17 @@ class OutsideController */ private $templating; + /** + * @param EngineInterface $templating + */ public function __construct(EngineInterface $templating) { $this->templating = $templating; } + /** + * @return Response + */ public function outsideAction() { return $this->templating->renderResponse('JenkoHouseBundle::outside.html.twig'); diff --git a/src/Jenko/HouseBundle/Resources/config/services.yml b/src/Jenko/HouseBundle/Resources/config/services.yml index 9fd370b..ad34542 100644 --- a/src/Jenko/HouseBundle/Resources/config/services.yml +++ b/src/Jenko/HouseBundle/Resources/config/services.yml @@ -12,12 +12,14 @@ services: class: %jenko.house.controllers.enter_room.class% arguments: - @templating - - @jenko.house.handlers.enter_room_handler + - @command_bus jenko.house.handlers.enter_room_handler: - class: Jenko\House\Handler\EnterRoomHandler + class: Jenko\HouseCommandHandling\Handler\EnterRoomHandler arguments: - @jenko.house.adapters.symfony_event_dispatcher + tags: + - { name: command_handler, handles: enter-room } jenko.house.adapters.symfony_event_dispatcher: class: Jenko\House\Adapter\SymfonyEventDispatcherAdapter @@ -35,4 +37,3 @@ services: class: Jenko\House\Adapter\PsrLoggerAdapter arguments: - @logger - \ No newline at end of file diff --git a/src/Jenko/HouseCommandHandling/Command/EnterRoomCommand.php b/src/Jenko/HouseCommandHandling/Command/EnterRoomCommand.php new file mode 100644 index 0000000..7e70aca --- /dev/null +++ b/src/Jenko/HouseCommandHandling/Command/EnterRoomCommand.php @@ -0,0 +1,21 @@ +house->enterRoom($command->room); $this->dispatcher->dispatch($house->releaseEvents()); - - return $house; } -} \ No newline at end of file +} diff --git a/src/Jenko/House/Handler/ExitRoomHandler.php b/src/Jenko/HouseCommandHandling/Handler/ExitRoomHandler.php similarity index 75% rename from src/Jenko/House/Handler/ExitRoomHandler.php rename to src/Jenko/HouseCommandHandling/Handler/ExitRoomHandler.php index eb8c45a..77c15fd 100644 --- a/src/Jenko/House/Handler/ExitRoomHandler.php +++ b/src/Jenko/HouseCommandHandling/Handler/ExitRoomHandler.php @@ -1,12 +1,14 @@ house->exitToRoom($command->room); $this->dispatcher->dispatch($house->releaseEvents()); return $house; } -} \ No newline at end of file +}