How to get started using Event Sourcing in an existing project

A while ago, we realized that Event Sourcing could perfectly fit our needs. I’m not going to write about how we came to this conclusion or what its benefits and disadvantages are. Today I’m gonna focus on how we stopped doing CRUD (and losing information) and how we got rid of a couple of our core DB tables.

Poor practical examples

I had never done it before, never applied these new concepts. However, I read a lot of blog posts on Event Sourcing, and I though I got (part of) the theory behind it. Unfortunately, practical examples were very difficult to find and some concepts remained a bit unclear.

Working code that cannot be easily changed

There were many interactions with those tables, tables full of precious and untouchable data. Even though the codebase was not that bad, it looked quite tough, or at least delicate, to even think to “move the truth” elsewhere. It was obvious it couldn’t be done all at once.

No time

There’s never time for refactoring or trying new ways out, but sometimes, avoiding it, could be even more costly than facing it.

What if it could be gradually adopted?

We defined a path and moved to the first step. The goal was to make the orders table a read model and be free to remove and rebuild it.

Yes, we’re an insurance company and there’s no “orders” table, but for the sake of simplicity I’ll use it for generic and naive examples.

Having a clear path to follow was very important because allowed to migrate without hurry, a little at a time.

Define events

First of all there was need to get a clear idea of what the meaningful events were.

We heard about Event Storming, a technique to explore the domain starting from domain events. We found out a lot of… “things” (there would be a lot to say), and it gave us the initial events from which we could begin.

Focusing on behaviours, the first events were modeled. They contained only data about state change.

{
  "event": "order_created",
  "id": 1,
  "customer_id": 2
}

{
  "event": "order_approved",
  "order_id": 1,
  "approved_at": "2017-03-01"
}

{
  "event": "order_shipped",
  "order_id": 1,
  "shipping_id": 3
}

Store and dispatch events

Introducing events into the code base was the next step, and it was a pretty easy one.

In order to start populating the event store, all we got to do was dispatch and store them, no side effects. Direct insert, update and delete operations on the orders table were still there, unharmed, for the time being.

Everything after this point were saved in both orders table and event store.

Projectors

What’s in the event store had to become the source of truth.

Given that a projector derives current state from the stream of events, we were able to update the orders table based on the events recorded in the event store.

We put a listener to the orderCreated event and started to let it add new records to the orders table. At the same time, the old code that inserted new order records was removed. From now on, it’s a projector responsibility. Running the projector began to be the only way to do it.

Past events

The next step was to generate and store also the orderCreated events of each old order. They were populated using information gathered from the orders table. That was a one-time process that allowed to get the complete history.

Other events

The orderCreated event contained only information existing at the very first moment of an order lifecycle. Therefore not all the orders table fields could have been populated using data from the event store.

We had to define all other events, so that all the fields were covered.

Every time a new event was introduced, the charge of updating specific orders table field passed to the projector.

Only when the last field was handled, the orders table finally became a persistent read model.

It’s done!

One event at a time, we got familiar with this new approach and today we couldn’t think of other ways to handle our orders. Once you start, you’ll see events everywhere!

Of course, we started to face new needs and challenges (versioning for example).

No, we’ve never dropped the orders table, but the awareness of being able to do it, it’s something you got to feel!

Davide - @davidefedrigo