Event Sourcing
in practice

Fork me on GitHub
Creative Commons License

Who we are

Benjamin Reitzammer

CTO at Vaamo Finanz AG

Johannes Seitz

Freelance Software Engineer

Who are you?

Before we get started...

This talk ...

... is for you, if you want to ...

Event Sourcing vs Active Record

Event Sourcing vs Active Record

We all know Active Record:

Object-Relational mapping

Event Sourcing is different

  • Event: Something that happend in the past.
  • E.G. Deposit Performed Event in UML

The advantages of Event Sourcing

The advantages of Event Sourcing

Mapping objects to tables is suprisingly hard

  • 143 pages on mapping objects to tables
  • Recommends using ORM-Tools only for CRUD-heavy applications
  • Why?

The Object-Relational mismatch

The biggest problem with ORM's is that they don't really map O to R. Tables _are not_ objects. They never were; and never will be.

How does Event Sourcing work?

Saving objects

Eventstream

Restoring objects

Subsequently apply the events from the respective EventStream to a "blank" object instance

Eventstream

Restoring objects

Restore from EventStream 1

Restoring objects

Restore from EventStream 2

Restoring objects

Restore from EventStream 3

Restoring objects

Restore from EventStream 4

Restore from EventStream 2

Updating objects

val account = accountRepository.get(123)
val modifiedAccount = account.withdraw(new Euro(10))
accountRepository.save(modifiedAccount)

What should be persisted?

Updating EventStream

Deleting objects

How do you delete an object?

Retroactive Event. An event undoing something that happened in the past

Making Event Sourcing work

Issues encountered in practice

Problem: Events vs Command

Event Sourcing

  • Persist only changes in state
  • Replay can be side-effect free

Command Sourcing

  • Persist Commands
  • Replay may trigger side-effects

Ex: Command

case class ChangeOwner(accountID: UUID, newOwner:String)

  extends Command



class BankAccountCommandHandler(repo:BankAccountRepository) {

  def handle(changeOwner:ChangeOwner) {

    val accountId = changeOwner.accountID

    val newOwner = changeOwner.newOwner

    val account = repo.getAccount(accountId)

    val modifiedAccount = account.changeOwner(newOwner)

    repo.saveAccount(modifiedAccount)

  }

}

Problem: Side-effects

Solutions

  • Solution 1: Separate side-effect and state change
  • Solution 2: Saving event triggers side-effect

Solution 1: Separate side-effect and state change

Only the event (=state change) is being replayed.

Solution 2: Saving events triggers side-effects

When events are saved, side-effects are triggered by event-listeners

Savings triggers side-effect

Problem: Reporting & Queries

The persisted event stream does not allow for database queries and reports

Solution

Use events to create "read models", optimised for queries and reports.

CQRS Image by Martin Fowler

Problem: Evolving events

How to evolve (immutable) events?

Reasons for change

Solution

Problem: Concurrent writes

How to resolve race conditions that occur due to concurrent writes?

Simple solution: Optimistic locking

Savings triggers side-effect

Summary

Considering Event Sourcing

Pros and Cons of Event/Command Sourcing

Pros

Cons

Debugging and accountability

Eventstream

Mining of event streams

Eventstream

Should I be doing Event Sourcing?

Event Sourcing is probably a good fit when ...

Interesting Projects

Event Store

Akka Persistence

Thank you for your kind attention

Sources and further reading

Event Sourcing theory

Event sourcing implementation

Sources and further reading

Command-Query Responsibility Segregation (CQRS)

Example Projects

/

#