Event Sourcing for Functional
Programmers

by Daniel Westheide / @kaffeecoder

@ flatMap(Oslo), 14.05.2013

Example application

https://github.com/dwestheide/eventhub-flatmap2013

git clone git://github.com/dwestheide/eventhub-flatmap2013.git
cd eventhub-flatmap2013
git checkout step-0
mongod
sbt run

Principles of Functional Programming

Immutable data


case class Meeting(
    id: MeetingId,
    name: String,
    time: DateTime,
    venue: Venue)
            

Pure functions


def moveToVenue(meeting: Meeting, newVenue: Venue): Meeting =
  meeting.copy(venue = newVenue)
            

Updating application state


meetingRepo.fromIdentity(meetingId).foreach { meeting =>
  meetingRepo.save(moveToVenue(meeting))
}
            
Wait, so our application state is effectively mutable? Doh!

Objections

In my domain, it is not important to know the previous states of an entity.
If my domain requires a history of what happened, of course I explicitly make that part of my domain model.

Event Sourcing

Aggregates

  • A cluster of related objects, both entities and value objects
  • One of the entities is the Aggregate Root
  • Only the root can be referenced from outside the aggregate

Aggregates

  • A boundary for transactional consistency
  • Must ensure that its state never ever violates its business invariants
  • Outside its boundary, there can only be eventual consistency
  • Only one aggregate can be modified in a single transaction

Example

Event Sourcing

  • Every state-modifying operation on an aggregate results in an event
  • The actual state change is achieved by applying the event to the aggregate

Producing events for state changes


def moveToVenue(
    meeting: Meeting,
    venue: Venue): Validation[String, (MeetingEvent, Meeting)] =
  if (canMoveToVenue(meeting, venue)) {
    val event = MovedToVenue(new DateTime(), meeting.id, venue)
    (event, this(meeting, event)).success
  } else s"Cannot move $meeting to $venue".fail
              

Applying an event


              (Aggregate, DomainEvent) => Aggregate
              

Applying an event


def apply(meeting: Meeting, event: MeetingEvent): Meeting =
  event match {
    case VenueChanged(_, _, aVenue) =>
      meeting.copy(venue = aVenue)
  }
              

Event Stream

Building up current state


def currentState(meetingId: MeetingId): Meeting =
  eventStream(meetingId).foldLeft(Meeting())(apply)
              

Can't change what was...

Rationale

  • Audit log for free
  • Analysis of historic data
  • Replaying events to examine errors
  • Flexibility: arbitrary new views or queries (with CQRS)
  • Enables Memory Image

CQRS

Command-Query Responsibility Segregation

CQRS

  • Command model: Validates and processes commands
  • Query model: various views on the data, denormalized
  • Can be scaled independently

CQRS with Event Sourcing

Memory Image

  • Keep application state in memory
  • Rebuild application state from event store
  • Aggregates in STM Refs

Single Writer Principle

Only a single service can process commands on an aggregate

See Single Writer Principle by Martin Thompson

Maybe we can use actors?

  • Event-driven
  • Can keep state
  • Process incoming messages one after another

Eventsourced

Building blocks

  • Processor
  • Channel
  • Journal

Let's get our hands dirty...