What is an event really? How can you best describe an event in your code? What types of events are there, and how do you decide whether or not to implement something as an event?
In this talk we start with a straightforward command-only piece of code. We extract events from it and start moving the event handling code out, trying different design patterns on the way. First we try Observer. Then we introduce event data, event handlers and a Mediator between our code and the event handlers. Finally we pick a well-known event dispatcher implementation (the Symfony EventDispatcher) and see how it uses the Chain of Responsibility design pattern to control the entire flow of a web application request.
In the end I will answer some burning questions like: is it safe to use events all over the place and rely on event handlers to do some really important stuff? How do I overcome the indirection in my event-driven code? And how can I quickly find out what happens where?
16. Observer pattern
Notify other parts of the application
when a change occurs
class PostService
{
function newCommentAdded()
{
foreach ($this->observers as $observer) {
$observer->notify();
}
}
}
27. Single responsibility
●
“Capitalize the comment!”:
PostService
●
“Use a different logger!”:
LoggerObserver
●
“Add a timestamp to the notification mail!”:
NotificationMailObserver
44. … to event handler
interface CommentAddedEventHandler
{
function handle(CommentAddedEvent $event);
}
45. Event handlers
class LoggingEventHandler implements
CommentAddedEventHandler
{
function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function handle(CommentAddedEvent $event)
{
$this->logger->info(
'New comment' . $event->comment()
);
}
}
46. Event handlers
class NotificationMailEventHandler implements
CommentAddedEventHandler
{
function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function handle(CommentAddedEvent $event)
{
$this->mailer->send(
'New comment: ' . $event->comment();
);
}
}
47. Configuration
class PostService
{
function __construct(array $eventHandlers)
{
$this->eventHandlers = $eventHandlers;
}
}
$postService = new PostService(
array(
new LoggingEventHandler($logger),
new NotificationMailEventHandler($mailer)
)
);
48. Looping over event handlers
class PostService
{
public function addComment($postId, $comment)
{
$this->newCommentAdded($postId, $comment);
}
function newCommentAdded($postId, $comment)
{
$event = new CommentAddedEvent(
$postId,
$comment
);
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->handle($event);
}
}
}
59. Events and application flow
Symfony2 uses events to generate
response for any given HTTP request
60. The HttpKernel
$request = Request::createFromGlobals();
// $kernel is in an instance of HttpKernelInterface
$response = $kernel->handle($request);
$response->send();
65. Special types of events
●
Kernel events are not merely
notifications
●
They allow other parts of the
application to step in and modify or
override behavior
66. Chain of responsibility
Handler 3Handler 1 Handler 2
Some sort
of request
Some sort
of request
Response
Some sort
of request
87. Just like...
●
A router determines the right controller
●
The service container injects the right
constructor arguments
●
And when you die, someone will bury your
body for you