exploring lightweight event sourcing - goto amsterdam 2011

108
Exploring Lightweight Event Sourcing Erik Rozendaal <[email protected] > @erikrozendaal GOTO Amsterdam 2011

Upload: erik-rozendaal

Post on 20-Jun-2015

3.068 views

Category:

Technology


0 download

DESCRIPTION

Exploring event sourcing in Scala in a lightweight manner, making it possible to build production ready applications.

TRANSCRIPT

Page 1: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring Lightweight Event Sourcing

Erik Rozendaal <[email protected]>@erikrozendaal

GOTO Amsterdam 2011

Page 2: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Goals

• The problem of data persistence

• Understand event sourcing

• Show why Scala is well-suited as implementation language

2

Page 3: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Problem

3

Page 4: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Data must be durable

4

Page 5: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

But current applications are lossy

5

Page 6: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 6

Lossy?

Page 7: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

• UPDATE invoice WHERE id = 1234 SET total_amount = 230

6

Lossy?

Page 8: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

• UPDATE invoice WHERE id = 1234 SET total_amount = 230

• What happened to the previous order amount?

6

Lossy?

Page 9: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

• UPDATE invoice WHERE id = 1234 SET total_amount = 230

• What happened to the previous order amount?

• Why was the order amount changed?

6

Lossy?

Page 10: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

• UPDATE invoice WHERE id = 1234 SET total_amount = 230

• What happened to the previous order amount?

• Why was the order amount changed?

• Application behavior is not captured!

6

Lossy?

Page 11: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Behavior?

7

Page 12: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00

Behavior?

7

Page 13: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Behavior?

7

Page 14: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Behavior?

7

Page 15: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

It’s just data mutation

8

Page 16: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

It’s just data mutation

8

Page 17: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

N-Tier

9

Page 18: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

N-Tier

9

• Presentation, Service, and Data Layers

Page 19: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

N-Tier

9

• Presentation, Service, and Data Layers

• Shared data model (“domain”)

Page 20: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

N-Tier

9

• Presentation, Service, and Data Layers

• Shared data model (“domain”)

• Heavy use of a single, global, mutable variable (“the database”)

Page 21: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Inexperienced programmers love magic because it saves their time. Experienced programmers hate magic because it wastes their time.” – @natpryce

10

Object-Relational Mapper

Page 22: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Transactions

• Start transaction

• SELECT copy of data from database

• ORM hydrates objects to give program private copy of data

• ORM compares mutated program copy with initial copy to generate UPDATEs

• Commit transaction

11

Page 23: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Performance Tuning

12

Page 24: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Performance Tuning

• Do you know the queries generated by your ORM?

12

Page 25: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Performance Tuning

• Do you know the queries generated by your ORM?

• What’s the query execution plan?

12

Page 26: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Performance Tuning

• Do you know the queries generated by your ORM?

• What’s the query execution plan?

• Optimize reads versus writes

12

Page 27: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Domain” Model

13

Page 28: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Domain” Model

• Presentation layer needs wide access to many parts

13

Page 29: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Domain” Model

• Presentation layer needs wide access to many parts

• Service layer is only interested in subset related to application behavior

13

Page 30: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Domain” Model

• Presentation layer needs wide access to many parts

• Service layer is only interested in subset related to application behavior

• ORM tightly couples domain to relational model

13

Page 31: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

“Domain” Model

• Presentation layer needs wide access to many parts

• Service layer is only interested in subset related to application behavior

• ORM tightly couples domain to relational model

• High coupling, low cohesion

13

Page 32: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Model

14

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Model

Page 33: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Model

14

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Model

Page 34: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Model

14

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

ModelService Layer

Page 35: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Model

14

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

ModelService Layer

Database

Page 36: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Is it any surprise that we’re struggling to build modular, maintainable

applications?

15

Page 37: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

• Domain-Driven Design An approach to software development that suggests that (1) For most software projects, the primary focus should be on the domain and domain logic; and (2) Complex domain designs should be based on a model.

• Domain Expert A member of a software project whose field is the domain of the application, rather than software development. Not just any user of the software, the domain expert has deep knowledge of the subject.

• Ubiquitous Language A language structured around the domain model and used by all team members to connect all the activities of the team with the software.

16

Domain Driven Design

Page 38: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Event Sourcing

17

• All state changes are explicitly captured using domain events

• Capture the intent of the user and the related data

• Events represent the outcome of application behavior

Page 39: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Source Control

18

Page 40: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Source Control

18

RCS

SCCS PVCS

CVSSubversion

Darcs GitMercurial

BitKeeper

Page 41: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Behavior

19

Page 42: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Draft Invoice Created

generate

Domain Behavior

19

Page 43: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00

Draft Invoice Created

generate

apply

Domain Behavior

19

Page 44: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

generate

generateapply

Domain Behavior

19

Page 45: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

generate

generateapply apply

Domain Behavior

19

Page 46: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

Item: "Food"Item amount: 9.95Total amount: 9.95

Invoice Item Added

generate

generate generateapply apply

Domain Behavior

19

Page 47: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

Item: "Food"Item amount: 9.95Total amount: 9.95

Invoice Item Added

generate

generate generateapply apply apply

Domain Behavior

19

Page 48: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Event Store

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

Item: "Food"Item amount: 9.95Total amount: 9.95

Invoice Item Added

Only the events need to be stored on disk

20

.......

Page 49: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Reloading from history

21

Page 50: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00

Draft Invoice Created

apply

Reloading from history

21

Page 51: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

apply apply

Reloading from history

21

Page 52: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

Item: "Food"Item amount: 9.95Total amount: 9.95

Invoice Item Added

apply apply apply

Reloading from history

21

Page 53: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Invoice

Status: "Draft"

Total: 0.00 Invoice

Status: "Draft"

Recipient: "Erik"

Total: 0.00

Draft Invoice Created

Recipient: "Erik"

Invoice Recipient Changed

Item: "Food"Item amount: 9.95Total amount: 9.95

Invoice Item Added

apply apply apply

Reloading from history

21

Page 54: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

YAGNI?

22

Page 55: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

YAGNI?

• On the checkout page we’d like to promote products that customers previously removed from their shopping cart.

22

Page 56: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

YAGNI?

• On the checkout page we’d like to promote products that customers previously removed from their shopping cart.

• Can you tell us how many people removed a product but bought it later anyway?

22

Page 57: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

YAGNI?

• On the checkout page we’d like to promote products that customers previously removed from their shopping cart.

• Can you tell us how many people removed a product but bought it later anyway?

• ... over the past 5 years? ... and how much time passed in-between?

22

Page 58: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

YAGNI?

• We have reports of a strange bug, but have not been able to isolate it. Could you look at the production data to find out what’s going on?

23

Page 59: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Is it worth it?

24

Page 60: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Is it worth it?

• Make the event sourcing implementation as simple as possible

24

Page 61: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Is it worth it?

• Make the event sourcing implementation as simple as possible

• ... while avoiding the complexities of databases, ORMs, etc.

24

Page 62: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Is it worth it?

• Make the event sourcing implementation as simple as possible

• ... while avoiding the complexities of databases, ORMs, etc.

• ... unless you want or need them :)

24

Page 63: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation

25

Page 64: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation• Events for durability

25

Page 65: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation• Events for durability

• Keep current state in RAM (Memory Image)

25

Page 66: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation• Events for durability

• Keep current state in RAM (Memory Image)

• Scala case classes to define events and immutable data structures

25

Page 67: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation• Events for durability

• Keep current state in RAM (Memory Image)

• Scala case classes to define events and immutable data structures

• Independent components composed into a single application

25

Page 68: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Simple functionality example

• “CRUD”: no domain logic, so no aggregate

• So we’ll persist events directly from the UI

• Useful to get started

• ... and even complex applications still contain simple functionality

26

Page 69: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

“CRUD”

Page 70: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

“CRUD”Create/Update/Delete

Page 71: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

“CRUD”Create/Update/Delete

Validate input and generate event

HTTP POST

Page 72: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”Create/Update/Delete

Validate input and generate event

HTTP POST

Page 73: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”Create/Update/Delete

Validate input and generate event

HTTP POST

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Presentation Model

Page 74: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”Create/Update/Delete

Render ViewQuery

Validate input and generate event

HTTP POST

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Presentation Model

Page 75: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”Create/Update/Delete

Read

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Presentation Model

Page 76: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Controller

28

post("/todo") { field("text", required) match { case Success(text) => commit(ToDoItemAdded(ToDoItem(UUID.randomUUID(), text))) redirect(url("/todo")) case Failure(error) => new ToDoView(toDoItems, Some(error)), } }

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

Page 77: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Memory Image

29

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

case class ToDoItems ( all : Map[UUID, ToDoItem] = Map.empty, recentlyAdded: Vector[UUID] = Vector.empty) { def apply(event: ToDoItemEvent) = event match { case ToDoItemAdded(item) => copy(all + (item.id -> item), recentlyAdded :+ item.id) // [... handle other event types ...] }

def mostRecent(count: Int) = recentlyAdded.takeRight(count).map(all).reverse}

Page 78: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

View

30

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

<table class="zebra-striped"> <thead><tr><th>To-Do</th></tr></thead> <tbody>{ for (item <- toDoItems.mostRecent(20)) yield { <tr><td>{ item.text }</td></tr> } }</tbody></table>

Page 79: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 31

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Domain Driven Design

Read

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

Page 80: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 31

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Domain Driven Design

Command

Domain Model

Read

Render ViewQueryHTTP GET

Validate input and generate event

HTTP POST

Page 81: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

32

sealed trait InvoiceEventcase class InvoiceCreated() extends InvoiceEventcase class InvoiceRecipientChanged(recipient: String) extends InvoiceEventcase class InvoiceItemAdded(item: InvoiceItem, totalAmount: BigDecimal) extends InvoiceEventcase class InvoiceSent(sentOn: LocalDate, paymentDueOn: LocalDate) extends InvoiceEvent

Page 82: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

33

case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

// [... other domain logic ...]

private def itemAdded = when[InvoiceItemAdded] { event => // ... }

}

Page 83: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

33

case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

// [... other domain logic ...]

private def itemAdded = when[InvoiceItemAdded] { event => // ... }

}

The “Brains”

Page 84: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

33

case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

// [... other domain logic ...]

private def itemAdded = when[InvoiceItemAdded] { event => // ... }

}

The “Brains”

The “Muscle”

Page 85: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

34

case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

// ...

def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = itemAdded(InvoiceItemAdded(InvoiceItem(nextItemId, description, amount), totalAmount + amount))

private def totalAmount = items.values.map(_.amount).sum

private def readyToSend_? = recipient.isDefined && items.nonEmpty

// ...

private def itemAdded = when[InvoiceItemAdded] { event => copy(nextItemId = nextItemId + 1, items = items + (event.item.id -> event.item)) }

}

Page 86: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Domain Code

35

case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

...

/** Reload from history. */ protected[this] def applyEvent = recipientChanged orElse itemAdded orElse sent

...}

Page 87: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Validate input and generate event

HTTP POST

36

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Cooperating UsersCreate/Update/Delete

Read

Render ViewQueryHTTP GET

Page 88: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Validate input and generate event

HTTP POST

36

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Cooperating Users

Read

Render ViewQueryHTTP GET

Conflict ResolutionSubmit Event

Conflicting Events

Page 89: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Validate input and generate event

HTTP POST

36

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Cooperating Users

Read

Render ViewQueryHTTP GET

Conflict ResolutionSubmit Event

Conflicting Events

Checkout revision 23

Page 90: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Validate input and generate event

HTTP POST

36

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Cooperating Users

Read

Render ViewQueryHTTP GET

Conflict ResolutionSubmit Event

Conflicting Events

Checkout revision 23

Submit eventsbased on revision 23

Page 91: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Validate input and generate event

HTTP POST

36

Event Store

Save Event

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Apply Event

Cooperating Users

Read

Render ViewQueryHTTP GET

Conflict ResolutionSubmit Event

Conflicting Events

Checkout revision 23

Submit eventsbased on revision 23

Compare submitted events with intermediate events baed on current revision

Page 92: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Events

Page 93: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

Events

Page 94: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

Events

Page 95: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

RDBMS

Events

Page 96: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

RDBMSSolr / Neo4J / HBase / ...

Events

Page 97: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

RDBMSSolr / Neo4J / HBase / ...

System Integration

Order Fullfillment Payment Provider

Legacy System

Events

Page 98: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

Replica

RDBMSSolr / Neo4J / HBase / ...

System Integration

Order Fullfillment Payment Provider

Legacy System

Events

Page 99: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

Replica

RDBMSSolr / Neo4J / HBase / ...

System Integration

Order Fullfillment Payment Provider

Legacy System

Events

Page 100: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing 37

Event Store

Invoice

Status: "Draft"

Recipient: "Erik"

Invoice Item

Total: 9.95

Item: "Food"

Amount: 9.95

Presentation Model

Push

Replica

RDBMSSolr / Neo4J / HBase / ...

System Integration

Order Fullfillment Payment Provider

Legacy System

Auditors

Events

Page 101: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Event Store

38

• Stores sequence of events

• Dispatches events when successfully committed

• Replays events on startup

• It’s your application’s transaction log

Page 102: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Writing Events to Disk

39

private class JournalFileWriter(file: File, var sequence: Long) { private val checksum = new CRC32() private val fileOutputStream = new FileOutputStream(file) private val dataOutputStream = new DataOutputStream( new CheckedOutputStream( new BufferedOutputStream(fileOutputStream), checksum))

def write(commit: Array[Byte]) { sequence += 1 checksum.reset() dataOutputStream.writeLong(sequence) dataOutputStream.writeInt(commit.size) dataOutputStream.write(commit) dataOutputStream.writeInt(checksum.getValue().toInt) }

def sync(metadata: Boolean = false) { dataOutputStream.flush() fileOutputStream.getChannel().force(metadata) } }

Page 104: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Implementation Limitations

• Single-threaded event store using disruptor pattern

• ~ 5.000 commits per second

• Number of events to replay on startup

• ~ 70.000 JSON serialized events/second

• ~ 200.000 protobuf serialized events/second

• Total number of objects to store in main memory

• JVM can run with large heaps (tens of gigabytes)

41

Page 105: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

But ready to scale

• Advanced event store implementations support SQL, MongoDB, Amazon SimpleDB, etc.

• Use persisted view model for high volume objects (fixes startup time and memory usage)

• Easy partitioning of aggregates (consistency boundary with unique identifier)

• Load aggregates on-demand, use snapshotting

42

Page 106: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

Conclusion

• Fully capture historical information

• Independent components where each part of the application can use its own data model

• Easier to fully understand compared to traditional ORM based approach

• Need to learn “Event-Driven” thinking

43

Page 107: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Thanks!

Page 108: Exploring Lightweight Event Sourcing - GOTO Amsterdam 2011

Exploring lightweight event sourcing

References• Example code https://github.com/erikrozendaal/scala-event-sourcing-example

• CQRS http://cqrsinfo.com/

• Greg Young, “Unshackle Your Domain”http://www.infoq.com/presentations/greg-young-unshackle-qcon08

• Pat Helland, “Life Beyond Distributed Transactions: An Apostate's Opinion” http://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf

• Erik Rozendaal, “Towards an immutable domain model” http://blog.zilverline.com/2011/02/10/towards-an-immutable-domain-model-monads-part-5/

• Martin Fowler, “Memory Image”, http://martinfowler.com/bliki/MemoryImage.html

• Martin Fowler, “The LMAX Architecture”, http://martinfowler.com/articles/lmax.html

45