Event Sourcing

The basic idea of Event Sourcing is to store a stream of events instead the current state of an object. So Event Sourcing is a different persistence approach.

Traditionally, we would store the current state of an object in a relational database. We would map the object to tables and columns and update them every time the object changes.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

classProduct{

val id=42

fun update(...){

this.name=“Coca-Cola”

this.price=1.99

}

}

repository.save(product)

ID|Name|Price

---|------------|----------

42|Coca-Cola|1.99

43|Water|0.99

Event Sourcing takes a different approach. Instead of saving the object, all changes to the object are saved. In Domain Driven Design and Event Sourcing those changes are called events. The sum of all events make up the current state of the object.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

classProduct{

val id=42

fun update(...){

...

returnProductUpdatedEvent(42,“Coca-Cola”,1.99)

}

}

eventStore.save(event)

ID|Type|Data

---|---------------------|-----------------------------

42|ProductCreatedEvent|{42,Cola,null}

42|PriceUpdatedEvent|{42,0.00}

42|ProductUpdatedEvent|{42,Coca-Cola,1.99}

43|ProductUpdatedEvent|{43,Water,0.99}

The benefit of this type of persistence is that we always keep the whole history of an object. We can see each and every change which occurred over time.

The downside is a more complicated and unfamiliar programming concept:

To implement Event Sourcing all changes must be published via events. Even the smallest change to our domain model (setting some flag from true to false) must result in an event. Any data which is not part of an event will be lost.

Having those events, we must implement two functionalities: (1) First we need a business method which takes an input, does things (calculations, calls, conversions, etc.) and finally throws an event with all data involved. (2) Second we need another method which takes the event and simply applies it to the model.

1

2

3

4

5

6

7

8

9

10

11

12

13

classProduct{

fun update(...){

// Business logic with calculations, calls, etc...

returnProductUpdatedEvent(...)

}

fun apply(ProductUpdatedEvent){

// Apply data! Don’t do anything else!

this.name=event.name

thisprice=event.price

}

}

The first method performs business logic and actually does something whereas the second function is just “a setter for data”. We need this differentiation, because whenever we load an object all events from the database must be applied to it. This must be done without triggering business logic and side effects.

1

2

val events=eventStore.findAllById(42)

val product=Product().applyAll(events)

Demo on GitHub

As this description might sound a little bit weird, I’ve prepared a small demo project on GitHub: