Events In Practice

GlasgowPHP - May 2017

$(whoami)

Scott Pringle

PHP Developer @ People's Postcode Lottery

DDDEdinburgh Organiser

What Is An Event?

An event is an action recognized by software, that may be handled by the software

https://en.wikipedia.org/wiki/Event_(computing)#Description

An Event is the encapsulation of a recorded action within an application

Me, probably plagiarising someone...

<?php

namespace Demo;

class UserRegistered
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function user(): User
    {
        return $this->user;
    }
}

Events have 0 or more listeners

<?php

namespace Demo;

class UserRegisteredListener
{
    private $emailer;

    public function __construct(Emailer $emailer)
    {
        $this->emailer = $emailer;
    }

    public function onUserRegistered(UserRegistered $event)    
    {
        $this->emailer->send(
            $event->user()->email(),
            'Subject',
            'Message'
        );
    }
}

Simple, right?

Thinking In Events

As Developers we...

  • Think in Actions
  • Something "will" happen

Context

  • Video Subscription Platform (E.G. Netflix)
    • Trials Are Available
    • Payments are not taken instantly
      • Payments get authorised & scheduled

User Registers

Add payment details

authorise payments

Add Subscription

Actions not events

thinking in events is hard!

Mindset change from "will" to "has"

Thinking in reactions, not actions

Event Driven Architecture

4 Principal Layers

  • Event Generator
  • Event Channel
  • Event Processing
  • Downstream Activity

Event Generator

  • Senses A Change
  • Frames The Change As An Event

Event Channel

  • Middleware from Event Generator
    to Event Processor
  • TCP/IP connection / Input file
  • Stores events into queues for Processors

Event Processing

  • Where the magic happens
  • 0 or more reactions
  • Could live outside application

Downstream Activity

  • Consequence of Event Processing, e.g.
    • Order confirmed emails being sent
    • Stock count being updated

Events in Frameworks & Libraries

Symfony HTTP Kernel

  • kernel.request
  • kernel.controller
  • kernel.view
  • kernel.response
  • kernel.terminate
  • kernel.exception

Doctrine

  • preRemove
  • postRemote
  • prePersist
  • postPersist
  • preUpdate
  • postUpdate
  • ...

All Of These Are Events Which Can Be Subscribed To / Published

I can't build an app using pre-defined events?!

Why not?

Custom Hooks Are Available

<?xml version="1.0" encoding="UTF-8"?>

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                          /Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">

    <entity name="User">

        <lifecycle-callbacks>
            <lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
            <lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
        </lifecycle-callbacks>

    </entity>

</doctrine-mapping>
<?php

namespace Demo;

class User
{
    // All the properties

    public function doStuffOnPrePersist()
    {
        $this->persisted = true;
    }

    public function doStuffOnPostPersist()
    {
        $this->persisted = false;
    }
}

I want more control

That I Can Agree With!

  • More explicit the events, the better
  • Control exactly where an event is triggered
    • Which controller, console command etc
  • Events mean more to the business
<?php

namespace Demo;

class UserRegisterController
{
    private $eventDispatcher;

    public function __construct(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    public function __invoke(Request $request)
    {
        $user = new User(...);

        $this->eventDispatcher->dispatch(
            'event.user_registered',
            new UserRegistered(
                $user
            )
        );

        return new JsonResponse();
    }
}
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="app.event.user_registered" class="Demo\EventListener\UserRegistered">
            <tag name="kernel.event_listener" event="event.user_registered" method="onUserRegistered" />
        </service>
    </services>
</container>

I've probably got that wrong...

Events Can be Used as Messages

Events can be handled asynchronously

Events should be immutable

Tell other parts of your application to do something

Create an audit trail...

Tell other applications to do something

  • Events Can Be Streamed
    • Kafka
    • RabbitMQ
    • Any Other Queuing System
    • File system

Event Sourcing

Events used to create state

No more relational tables

State becomes rebuildable

Predict state with Mocked events

Complete application history

You don't think of an audit log until you're audited

Treat events As first class citizens

Driven by Problem Domains

Map to things that really happen in your organisation

Business People don't care about technology

They only care about results

Questions?

Thank you

Twitter: @Luciam91

http://slack.scotlandphp.co.uk

Events In Practice

By Scott Pringle

Events In Practice

  • 1,446