One of the DPC14 talks I was fortunate enough to attend was entitled “Practical Event Sourcing”. I found it so good that I’m going to attempt to summarise a very excellent 45-minute talk into about 15 minutes of code. Here goes! Details of the talk can be found at http://joind.in/talk/view/10879, and I’m grateful to Mathias Verraes for schooling me. Go thank him for this! The code for this chapter can be found at https://github.com/connectjoepublic/

tutorial-event-sourcing. If you spot any errors or typos, please comment on this post, or create issue at the repository. This tutorial requires PHP 5.4 or greater.

Grommets and Glue

One day I was building an online shop. I had a list of products (their prices, sizes, photos and descriptions), a domain name and a bit of time. So I built a database to represent the product catalog. I build a few customer-related tables to track things like profiles and orders. I integrated a shopping cart. In the end, I had a functional online shop. Success!

Some time went by, and I got an email:

Hi Chris, We, here at Household Grommets and Glue, would like to know how many of our customers have stopped short of purchasing a shopping basket full of our wonderful merchandise. Janice (in accounting) told us that her mother has often not purchased something because of cold feet. We want to know if this is caused by a bad online experience, and whether it can be remedied by the addition of an online foot-ware section. Yours non-refundably,

Joe, from the corner office

That’s easy, I thought. I’ll just add some tracking to the shop, and Household Grommets and Glue can get data they need (so that Janice’s mom can get the warm feet she deserves).

Unfortunately this would mean a few months of data-gathering. This is because what was being stored (in the database) was state, and what was required was a determination of behaviour.

Behaviour and State

Household Grommets and Glue doesn’t exist. I made it up to illustrate a situation quite common to our profession. We build large applications which store a lot of state. They do that because it’s generally useful. Except when it’s not.

State is just a common term for the particular condition something is in at any given time. The state of a tax form is the combination of fields which are filled with information. The state of a bus is the number and combination of occupied vs. vacant seats.

The entire process of using an application is behaviour. Creating a shopping basket, adding products to it, removing products from it and checking out are all behavioural.

Yet all that we store is state! We can tell if there are currently products in a shopping basket. We can tell if there are currently completed orders that need processed. What we can’t tell is how we got there.

This leads to the idea of event-sourcing…

Event Sourcing

Event Sourcing is just a fancy name for storing behaviour (events) first, and state second. Instead of storing the current state of objects we store the events which describe how they got to that state. We can store cached states, but they’re just throw-away data for rapid querying.

Aggregates and Events

To keep this post simple, I’m going to use customers as an example:

class CustomerRegistered

{

/**

* @var string

*/

protected $name;



/**

* @var string

*/

protected $surname;



/**

* @var string

*/

protected $emailAddress;



/**

* @param string $name

* @param string $surname

* @param string $email

*/

public function __construct($name, $surname, $email)

{

$this->name = $name;

$this->surname = $surname;

$this->email = $email;

}



/**

* @return string

*/

public function getName()

{

return $this->name;

}



/**

* @return string

*/

public function getSurname()

{

return $this->surname;

}



/**

* @return string

*/

public function getEmail()

{

return $this->email;

}

}

This simple event describes a customer having registered. Their name, surname and email are all required fields, and are used in a manner that may remind you of dependency injection.

We can create a new instance of this event simply:

new CustomerRegistered(

"Christopher", "Pitt",

"chris@connectjoepublic.com"

);

Let’s define another event, for when customers update their profiles:

class CustomerUpdatedProfile

{

/**

* @var Customer

*/

protected $customer;



/**

* @var string

*/

protected $field;



/**

* @var mixed

*/

protected $value;



/**

* @param Customer $customer

* @param string $field

* @param mixed $value

*/

public function __construct(

Customer $customer,

$field,

$value

)

{

$this->customer = $customer;

$this->field = $field;

$this->value = $value;

}



/**

* @return Customer

*/

public function getCustomer()

{

return $this->customer;

}



/**

* @return string

*/

public function getField()

{

return $this->field;

}



/**

* @return mixed

*/

public function getValue()

{

return $this->value;

}

}

A series of these events may look something like:

new CustomerRegistered(

"Christopher", "Pitt",

"chris@connectjoepublic.com"

);



new CustomerUpdatedProfile(

$customer,

"name", "Chris"

);



new CustomerUpdatedProfile(

$customer,

"email", "chris@connect.joepublic.com"

);



new CustomerClosedAccount(

$customer

);

The $customer seems to be the only thing collecting these events together, yet we’ve not created it yet. In DDD-land; the $customer is an aggregate. That is, the Customer class is an entity which acts as the public API to these underlying events:

class Customer

{

public static function register($name, $surname, $email)

{

$customer = new self();

return $customer;

}

}

Though not very useful, this gives an expressive API:

$customer = Customer::register(

"Christopher", "Pitt",

"chris@joepublicn.com"

);

We can further extend the Customer class; to record that it was created:

class Customer

{

/**

* @var array

*/

protected $events = [];



private function __construct()

{

/*

* We make this private so that

* it will not be poorly created.

*/

}



/**

* @param $name

* @param $surname

* @param $email

*

* @return Customer

*/

public static function register($name, $surname, $email)

{

$customer = new self();



$customer->recordThat(

new CustomerRegistered($name, $surname, $email)

);



return $customer;

}



/**

* @param $event

*/

public function recordThat($event)

{

$this->events[] = $event;

}



/**

* @return array

*/

public function getEvents()

{

return $this->events;

}

}

Now we can add new events and retrieve all the events contained in the aggregate:

$customer = Customer::register(

"Christopher", "Pitt",

"chris@connectjoepublic.com"

);



$customer->recordThat(

new CustomerUpdatedProfile(

$customer, "name", "Chris"

)

);



$customer->getEvents(); // array of events

Alternatively, we can encapsulate more of the entity’s behaviour, by adding methods to the Customer class:

class Customer

{

...other code



/**

* @param string $field

* @param mixed $value

*/

public function updateProfile($field, $value)

{

$this->recordThat(

new CustomerUpdatedProfile(

$this, $field, $value

)

);

}

}

Usage then becomes:

$customer = Customer::register(

"Christopher", "Pitt",

"chris@connectjoepublic.com"

);



$customer->updateProfile("name", "Chris");



$customer->getEvents(); // array of events

So this is starting to look just like our old CRUD code. One significant difference is that we can’t access the latest customer state (via the aggregate class), until we add:

class Customer

{

...other code



/**

* @var string

*/

protected $name;



/**

* @var string

*/

protected $surname;



/**

* @var string

*/

protected $email;



/**

* @param $name

* @param $surname

* @param $email

*

* @return Customer

*/

public static function register($name, $surname, $email)

{

$customer = new self();



$customer->name = $name;

$customer->surname = $surname;

$customer->email = $email;



$customer->recordThat(

new CustomerRegistered($name, $surname, $email)

);



return $customer;

}



/**

* @param string $field

* @param mixed $value

*/

public function updateProfile($field, $value)

{

$this->$field = $value;



$this->recordThat(

new CustomerUpdatedProfile(

$this, $field, $value

)

);

}



/**

* @return string

*/

public function getName()

{

return $this->name;

}



/**

* @return string

*/

public function getSurname()

{

return $this->surname;

}



/**

* @return string

*/

public function getEmail()

{

return $this->email;

}

}

It’s a little bit of repetition, but the result is access to all the behaviour as well as the current state. We’re also close to being able to regenerate the state from behaviour:

class Customer

{

...other code



/**

* @param array $events

*

* @return self

*/

public static function regenerateFrom(array $events)

{

$customer = new self();



foreach ($events as $event) {

$customer->recordThat($event);

}



return $customer;

}

}

Unfortunately, this prevents the register() method from being executed, and the state from being set. What we need is a way to apply state when new aggregates are created, or when they are regenerated:

class Customer

{

...other code



/**

* @param $name

* @param $surname

* @param $email

*

* @return Customer

*/

public static function register($name, $surname, $email)

{

$customer = new self();



$customer->recordThat(

new CustomerRegistered($name, $surname, $email)

);



return $customer;

}



/**

* @param $event

*/

public function recordThat($event)

{

$this->events[] = $event;

$this->apply($event);

}



/**

* @param string $field

* @param mixed $value

*/

public function updateProfile($field, $value)

{

$this->recordThat(

new CustomerUpdatedProfile(

$this, $field, $value

)

);

}



/**

* @param $event

*/

protected function apply($event)

{

$method = "apply" . get_class($event);

$this->$method($event);

}



/**

* @param $event

*/

protected function applyCustomerRegistered($event)

{

$this->name = $event->getName();

$this->surname = $event->getSurname();

$this->email = $event->getEmail();

}



/**

* @param $event

*/

protected function applyCustomerUpdatedProfile($event)

{

$field = $event->getField();

$this->$field = $event->getValue();

}

}

We’ve moved all the state-specific code into methods which handle the application of events. Notice we no longer set the properties of any $customer objects from the outside, but rather as changes brought on as events occur.

We could now even split the aggregate code from the entity code and it wouldn’t break anything.

Delegators and Projectors

We like state because it’s easy to reason about. I prefer to describe a customer by their current state, than to describe them by the list of events which brought them to the state they are currently in. That’s because being defined by state is more efficient than being defined by behaviour.

We see this when it comes to things like SQL. We select records by their state, not by the actions performed to get them to a certain state. Consequently, we would struggle to read data from the same model we’re writing to.

I spoke earlier about the throw-away data we can also generate. To do that, we need to build state from events, in the same way that we’re doing it in the Customer class. The first step towards this is creating an event dispatcher:

class Dispatcher

{

...other code



/**

* @var array

*/

protected $events = [];



/**

* @param string $key

* @param callable $callback

*/

public function listen($key, $callback)

{

$this->events[$key][] = $callback;

}



/**

* @param string $key

* @param mixed $event

*/

public function dispatch($key, $event)

{

if (isset($this->events[$key])) {

foreach ($this->events[$key] as $callback) {

$callback($event);

}

}

}

}

I’m avoiding many obvious aspects of this dispatcher (like error handling and callback removal). They won’t add much to this post, so feel free to add them on your own time.

Next, we need to dispatch events from our aggregate:

class Customer

{

...other code



/**

* @var Dispatcher

*/

protected $dispatcher;



/**

* @param Dispatcher $dispatcher

*/

private function __construct(Dispatcher $dispatcher)

{

/*

* We make this private so that

* it will not be poorly created.

*/



$this->dispatcher = $dispatcher;

}



/**

* @param Dispatcher $dispatcher

* @param string $name

* @param string $surname

* @param string $email

*

* @return Customer

*/

public static function register(

$dispatcher,

$name,

$surname,

$email

)

{

$event = new CustomerRegistered($name, $surname, $email);



$customer = new self($dispatcher);

$customer->recordThat($event);

$customer->dispatch($event);



return $customer;

}



/**

* @param mixed $event

*/

protected function dispatch($event) {

$this->dispatcher->dispatch(

get_class($event), $event

);

}



/**

* @param string $field

* @param mixed $value

*/

public function updateProfile($field, $value)

{

$event = new CustomerUpdatedProfile(

$this, $field, $value

);



$this->recordThat($event);

$this->dispatch($event);

}



/**

* @param Dispatcher $dispatcher

* @param array $events

*

* @return self

*/

public static function regenerateFrom(

Dispatcher $dispatcher,

array $events

)

{

$customer = new self($dispatcher);



foreach ($events as $event) {

$customer->recordThat($event);

}



return $customer;

}

}

This gives us the means to listen for customer events, and execute arbitrary callbacks when they happen. We can combine this with a projector class, to populate a persistent store:

class CustomerProjector

{

/**

* @var CustomerRepository

*/

protected $repository;



/**

* @param CustomerRepository $repository

*/

public function __construct(CustomerRepository $repository)

{

$this->repository = $repository;

}



/**

* @param CustomerRegistered $event

*/

public function customerRegistered(CustomerRegistered $event)

{

$this->repository->create(

$event->getName(),

$event->getSurname(),

$event->getEmail()

);

}



/**

* @param CustomerUpdatedProfile $event

*/

public function customerUpdatedProfile(

CustomerUpdatedProfile $event

)

{

$customer = $this->repository->findByEmail(

$event->getCustomer()->getEmail()

);



$this->repository->update(

$customer,

$event->getField(),

$event->getValue()

);

}

}

We can combine this with the simple usage we had:

$dispatcher = new Dispatcher();



$repository = new CustomerRepository();



$projector = new CustomerProjector($repository);



$dispatcher->listen(

"CustomerRegistered",

[$projector, "customerRegistered"]

);



$dispatcher->listen(

"CustomerUpdatedProfile",

[$projector, "customerUpdatedProfile"]

);



$customer = Customer::register(

$dispatcher,

"Christopher", "Pitt",

"chris@connectjoepublic.com"

);



$customer->updateProfile("name", "Chris");

…and be assured that we’ll have all the redundant SQL data, for querying.

I’ve stopped just short of storing the cached state (the results of the projector methods) and the events to a persistent store. The beauty of this approach is that they don’t even need to use the same type of persistent store for both. So long as your events (CustomerRegistered, CustomerUpdatedProfile etc.) are maintained, you can generate any amount of cached state you require.

In 6 months, when Household Grommets and Glue ask you how many unfulfilled orders there were; you can just create a new projector, and replay events to generate the answers to their questions…