Permalink
Browse files

Version22

The main thing I did in this commit was to add an event dispatcher. I
throw a domain event in my model when entering a room and needed the
dispatcher in place to support that.

As I am using Symfony it made sense to me to use the Symfony event
dispatcher. However, it is a bit of a pain because it expects to have a
name and to be `Symfony\Component\EventDispatcher\Event`, as I was
trying to follow the clean architecture and have no outer dependencies I
didn't want to have my event extend
`Symfony\Component\EventDispatcher\Event`. Therefore, I needed to create
an adapter. Going back to the Raspberry Pi analogy made in a previous
version. I needed a serial to usb adapter, or something like that.

I also wanted to use my own Dispatcher interface, because what if I
wanted to stop using the Symfony dispatcher and replace it with
something else? I want to be able to continue using the same interface
but just add a new adapter and change my DI. You can see how I achieved
this from the code within this commit.

Finally, I added a `HandlerInterface` so that I can swap out handlers
using DI should I need to.
  • Loading branch information...
jenkoian committed Jan 19, 2015
1 parent 73bb68c commit 2c2ec90ebf0d55b5bf30366df99a1dfd31e14077
@@ -10,7 +10,7 @@ class HomeAloneHouseFactorySpec extends ObjectBehavior
function it_is_initializable()
{
$this->shouldHaveType('Jenko\House\Factory\HomeAloneHouseFactory');
$this->shouldHaveType('Jenko\House\Factory\HouseFactory');
$this->shouldHaveType('Jenko\House\Factory\HouseFactoryInterface');
}
function it_should_return_a_house()
@@ -0,0 +1,34 @@
<?php
namespace spec\Jenko\House\Handler;
use Jenko\House\Command\EnterRoomCommand;
use Jenko\House\Event\EventDispatcherInterface;
use Jenko\House\Factory\HomeAloneHouseFactory;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class EnterRoomHandlerSpec extends ObjectBehavior
{
function let(EventDispatcherInterface $dispatcher)
{
$this->beConstructedWith($dispatcher);
}
function it_is_initializable()
{
$this->shouldHaveType('Jenko\House\Handler\EnterRoomHandler');
}
function it_dispatches_events(EventDispatcherInterface $dispatcher)
{
$house = HomeAloneHouseFactory::getHouse();
$house->enterRoom('hallway');
$command = new EnterRoomCommand();
$command->room = 'kitchen';
$dispatcher->dispatch(Argument::any())->shouldBeCalled();
$this->handle($command);
}
}
@@ -0,0 +1,34 @@
<?php
namespace spec\Jenko\House\Handler;
use Jenko\House\Command\ExitRoomCommand;
use Jenko\House\Event\EventDispatcherInterface;
use Jenko\House\Factory\HomeAloneHouseFactory;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ExitRoomHandlerSpec extends ObjectBehavior
{
function let(EventDispatcherInterface $dispatcher)
{
$this->beConstructedWith($dispatcher);
}
function it_is_initializable()
{
$this->shouldHaveType('Jenko\House\Handler\ExitRoomHandler');
}
function it_dispatches_events(EventDispatcherInterface $dispatcher)
{
$house = HomeAloneHouseFactory::getHouse();
$house->enterRoom('hallway');
$command = new ExitRoomCommand();
$command->room = 'kitchen';
$dispatcher->dispatch(Argument::any())->shouldBeCalled();
$this->handle($command);
}
}
@@ -0,0 +1,10 @@
<?php
namespace Jenko\House\Adapter;
use Symfony\Component\EventDispatcher\Event;
class SymfonyEventAdapter extends Event
{
}
@@ -0,0 +1,30 @@
<?php
namespace Jenko\House\Adapter;
use Jenko\House\Event\EventDispatcherInterface as LocalEventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class SymfonyEventDispatcherAdapter implements LocalEventDispatcherInterface
{
/**
* @var EventDispatcherInterface
*/
protected $dispatcher;
/**
* @param EventDispatcherInterface $dispatcher
*/
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public function dispatch(array $events)
{
foreach ($events as $event) {
$this->dispatcher->dispatch($event::NAME, $event);
}
}
}
@@ -2,8 +2,12 @@
namespace Jenko\House\Event;
class EnteredRoomEvent
use Jenko\House\Adapter\SymfonyEventAdapter;
class EnteredRoomEvent extends SymfonyEventAdapter
{
const NAME = 'house.room-entered';
public $roomName;
public $enteredOn;
@@ -0,0 +1,12 @@
<?php
namespace Jenko\House\Event;
interface EventDispatcherInterface
{
/**
* @param array $events
* @return mixed
*/
public function dispatch(array $events);
}
@@ -0,0 +1,32 @@
<?php
namespace Jenko\House\Event;
trait EventGenerator
{
/**
* @var array
*/
protected $pendingEvents = [];
/**
* Raise a new event
*
* @param $event
*/
public function raiseEvent($event)
{
$this->pendingEvents[] = $event;
}
/**
* @return array
*/
public function releaseEvents()
{
$events = $this->pendingEvents;
$this->pendingEvents = [];
return $events;
}
}
@@ -11,7 +11,7 @@
* Note: Usually you would have some kind of persistence mechanism. However, in our case a static factory (essentially
* a singleton) is fine, as the house itself won't change.
*/
final class HomeAloneHouseFactory implements HouseFactory
final class HomeAloneHouseFactory implements HouseFactoryInterface
{
/**
* @var House $house
@@ -5,7 +5,7 @@
use Jenko\House\House;
use Jenko\House\Location;
interface HouseFactory
interface HouseFactoryInterface
{
/**
* @param Location|string|null $currentLocation
@@ -2,15 +2,26 @@
namespace Jenko\House\Handler;
use Jenko\House\Event\EventDispatcherInterface;
use Jenko\House\Factory\HomeAloneHouseFactory;
use Jenko\House\House;
final class EnterRoomHandler
final class EnterRoomHandler implements HandlerInterface
{
/**
* @var House $house
*/
private $house;
public function __construct()
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->house = HomeAloneHouseFactory::getHouse();
$this->dispatcher = $dispatcher;
}
/**
@@ -19,6 +30,9 @@ public function __construct()
*/
public function handle($command)
{
return $this->house->enterRoom($command->room);
$house = $this->house->enterRoom($command->room);
$this->dispatcher->dispatch($house->releaseEvents());
return $house;
}
}
}
@@ -2,15 +2,26 @@
namespace Jenko\House\Handler;
use Jenko\House\Event\EventDispatcherInterface;
use Jenko\House\Factory\HomeAloneHouseFactory;
use Jenko\House\House;
final class ExitRoomHandler
final class ExitRoomHandler implements HandlerInterface
{
/**
* @var House $house
*/
private $house;
public function __construct()
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->house = HomeAloneHouseFactory::getHouse();
$this->dispatcher = $dispatcher;
}
/**
@@ -19,6 +30,9 @@ public function __construct()
*/
public function handle($command)
{
return $this->house->exitToRoom($command->room);
$house = $this->house->exitToRoom($command->room);
$this->dispatcher->dispatch($house->releaseEvents());
return $house;
}
}
}
@@ -0,0 +1,12 @@
<?php
namespace Jenko\House\Handler;
interface HandlerInterface
{
/**
* @param $command
* @return mixed
*/
public function handle($command);
}
@@ -2,10 +2,14 @@
namespace Jenko\House;
use Jenko\House\Event\EnteredRoomEvent;
use Jenko\House\Event\EventGenerator;
use Jenko\House\Exception\LocationDoesNotExistException;
final class House
{
use EventGenerator;
/**
* @var array
*/
@@ -80,6 +84,8 @@ public function enterRoom($room)
$this->previousLocation = $this->currentLocation;
$this->currentLocation = $room;
$this->raiseEvent(new EnteredRoomEvent($this->currentLocation->getName()));
return $this;
}
@@ -3,7 +3,7 @@
namespace Jenko\HouseBundle\Controller;
use Jenko\House\Command\EnterRoomCommand;
use Jenko\House\Handler\EnterRoomHandler;
use Jenko\House\Handler\HandlerInterface;
use Jenko\House\House;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -16,12 +16,18 @@ class EnterRoomController
*/
private $templating;
/**
* @var HandlerInterface $handler
*/
private $handler;
/**
* @param EngineInterface $templating
*/
public function __construct(EngineInterface $templating)
public function __construct(EngineInterface $templating, HandlerInterface $handler)
{
$this->templating = $templating;
$this->handler = $handler;
}
/**
@@ -34,9 +40,8 @@ public function enterAction(Request $request)
$command = new EnterRoomCommand();
$command->room = $request->get('location');
$handler = new EnterRoomHandler();
/** @var House $house */
$house = $handler->handle($command);
$house = $this->handler->handle($command);
return $this->templating->renderResponse(
'JenkoHouseBundle::room.html.twig',
@@ -11,4 +11,15 @@ services:
jenko.house.controllers.enter_room:
class: %jenko.house.controllers.enter_room.class%
arguments:
- @templating
- @templating
- @jenko.house.handlers.enter_room_handler
jenko.house.handlers.enter_room_handler:
class: Jenko\House\Handler\EnterRoomHandler
arguments:
- @jenko.house.adapters.symfony_event_dispatcher
jenko.house.adapters.symfony_event_dispatcher:
class: Jenko\House\Adapter\SymfonyEventDispatcherAdapter
arguments:
- @event_dispatcher

0 comments on commit 2c2ec90

Please sign in to comment.