Exploring Lightweight Event Sourcing Erik Rozendaal - - PowerPoint PPT Presentation

exploring lightweight event sourcing
SMART_READER_LITE
LIVE PREVIEW

Exploring Lightweight Event Sourcing Erik Rozendaal - - PowerPoint PPT Presentation

Exploring Lightweight Event Sourcing Erik Rozendaal <erozendaal@zilverline.com> @erikrozendaal GOTO Amsterdam 2011 Goals The problem of data persistence Understand event sourcing Show why Scala is well-suited as


slide-1
SLIDE 1

Exploring Lightweight Event Sourcing

Erik Rozendaal <erozendaal@zilverline.com> @erikrozendaal

GOTO Amsterdam 2011

slide-2
SLIDE 2

Exploring lightweight event sourcing

Goals

  • The problem of data persistence
  • Understand event sourcing
  • Show why Scala is well-suited as

implementation language

2

slide-3
SLIDE 3

Exploring lightweight event sourcing

Problem

3

slide-4
SLIDE 4

Exploring lightweight event sourcing

Data must be durable

4

slide-5
SLIDE 5

Exploring lightweight event sourcing

But current applications are lossy

5

slide-6
SLIDE 6

Exploring lightweight event sourcing 6

Lossy?

slide-7
SLIDE 7

Exploring lightweight event sourcing

  • UPDATE invoice WHERE id = 1234

SET total_amount = 230

6

Lossy?

slide-8
SLIDE 8

Exploring lightweight event sourcing

  • UPDATE invoice WHERE id = 1234

SET total_amount = 230

  • What happened to the previous order

amount?

6

Lossy?

slide-9
SLIDE 9

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?

slide-10
SLIDE 10

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?

slide-11
SLIDE 11

Exploring lightweight event sourcing

Behavior?

7

slide-12
SLIDE 12

Exploring lightweight event sourcing

Invoice Status: "Draft" Total: 0.00

Behavior?

7

slide-13
SLIDE 13

Exploring lightweight event sourcing

Invoice Status: "Draft" Total: 0.00 Invoice Status: "Draft" Recipient: "Erik" Total: 0.00

Behavior?

7

slide-14
SLIDE 14

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

slide-15
SLIDE 15

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

slide-16
SLIDE 16

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

slide-17
SLIDE 17

Exploring lightweight event sourcing

N-Tier

9

slide-18
SLIDE 18

Exploring lightweight event sourcing

N-Tier

9

  • Presentation, Service, and Data Layers
slide-19
SLIDE 19

Exploring lightweight event sourcing

N-Tier

9

  • Presentation, Service, and Data Layers
  • Shared data model (“domain”)
slide-20
SLIDE 20

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”)

slide-21
SLIDE 21

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

slide-22
SLIDE 22

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

slide-23
SLIDE 23

Exploring lightweight event sourcing

Performance Tuning

12

slide-24
SLIDE 24

Exploring lightweight event sourcing

Performance Tuning

  • Do you know the queries generated by

your ORM?

12

slide-25
SLIDE 25

Exploring lightweight event sourcing

Performance Tuning

  • Do you know the queries generated by

your ORM?

  • What’s the query execution plan?

12

slide-26
SLIDE 26

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

slide-27
SLIDE 27

Exploring lightweight event sourcing

“Domain” Model

13

slide-28
SLIDE 28

Exploring lightweight event sourcing

“Domain” Model

  • Presentation layer needs wide access to

many parts

13

slide-29
SLIDE 29

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

slide-30
SLIDE 30

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

slide-31
SLIDE 31

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

slide-32
SLIDE 32

Exploring lightweight event sourcing

Domain Model

14

Invoice Status: "Draft" Recipient: "Erik" Invoice Item Total: 9.95 Item: "Food" Amount: 9.95

Model

slide-33
SLIDE 33

Exploring lightweight event sourcing

Domain Model

14

Invoice Status: "Draft" Recipient: "Erik" Invoice Item Total: 9.95 Item: "Food" Amount: 9.95

Model

slide-34
SLIDE 34

Exploring lightweight event sourcing

Domain Model

14

Invoice Status: "Draft" Recipient: "Erik" Invoice Item Total: 9.95 Item: "Food" Amount: 9.95

Model Service Layer

slide-35
SLIDE 35

Exploring lightweight event sourcing

Domain Model

14

Invoice Status: "Draft" Recipient: "Erik" Invoice Item Total: 9.95 Item: "Food" Amount: 9.95

Model Service Layer

Database

slide-36
SLIDE 36

Exploring lightweight event sourcing

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

15

slide-37
SLIDE 37

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

  • n a model.
  • Domain Expert A member of a software project whose field is the domain
  • f 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

slide-38
SLIDE 38

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

slide-39
SLIDE 39

Exploring lightweight event sourcing

Source Control

18

slide-40
SLIDE 40

Exploring lightweight event sourcing

Source Control

18

RCS SCCS PVCS CVS Subversion Darcs Git Mercurial BitKeeper

slide-41
SLIDE 41

Exploring lightweight event sourcing

Domain Behavior

19

slide-42
SLIDE 42

Exploring lightweight event sourcing

Draft Invoice Created

generate

Domain Behavior

19

slide-43
SLIDE 43

Exploring lightweight event sourcing

Invoice Status: "Draft" Total: 0.00 Draft Invoice Created

generate apply

Domain Behavior

19

slide-44
SLIDE 44

Exploring lightweight event sourcing

Invoice Status: "Draft" Total: 0.00 Draft Invoice Created Recipient: "Erik" Invoice Recipient Changed

generate generate apply

Domain Behavior

19

slide-45
SLIDE 45

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 generate apply apply

Domain Behavior

19

slide-46
SLIDE 46

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.95 Total amount: 9.95 Invoice Item Added

generate generate generate apply apply

Domain Behavior

19

slide-47
SLIDE 47

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.95 Total amount: 9.95 Invoice Item Added

generate generate generate apply apply apply

Domain Behavior

19

slide-48
SLIDE 48

Exploring lightweight event sourcing

Event Store

Draft Invoice Created Recipient: "Erik" Invoice Recipient Changed Item: "Food" Item amount: 9.95 Total amount: 9.95 Invoice Item Added

Only the events need to be stored on disk

20

.......

slide-49
SLIDE 49

Exploring lightweight event sourcing

Reloading from history

21

slide-50
SLIDE 50

Exploring lightweight event sourcing

Invoice Status: "Draft" Total: 0.00 Draft Invoice Created

apply

Reloading from history

21

slide-51
SLIDE 51

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

slide-52
SLIDE 52

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.95 Total amount: 9.95 Invoice Item Added

apply apply apply

Reloading from history

21

slide-53
SLIDE 53

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.95 Total amount: 9.95 Invoice Item Added

apply apply apply

Reloading from history

21

slide-54
SLIDE 54

Exploring lightweight event sourcing

YAGNI?

22

slide-55
SLIDE 55

Exploring lightweight event sourcing

YAGNI?

  • On the checkout page we’d like to promote

products that customers previously removed from their shopping cart.

22

slide-56
SLIDE 56

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

slide-57
SLIDE 57

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

slide-58
SLIDE 58

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

slide-59
SLIDE 59

Exploring lightweight event sourcing

Is it worth it?

24

slide-60
SLIDE 60

Exploring lightweight event sourcing

Is it worth it?

  • Make the event sourcing implementation as

simple as possible

24

slide-61
SLIDE 61

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

slide-62
SLIDE 62

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

slide-63
SLIDE 63

Exploring lightweight event sourcing

Implementation

25

slide-64
SLIDE 64

Exploring lightweight event sourcing

Implementation

  • Events for durability

25

slide-65
SLIDE 65

Exploring lightweight event sourcing

Implementation

  • Events for durability
  • Keep current state in RAM

(Memory Image)

25

slide-66
SLIDE 66

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

slide-67
SLIDE 67

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

slide-68
SLIDE 68

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

slide-69
SLIDE 69

Exploring lightweight event sourcing 27

“CRUD”

slide-70
SLIDE 70

Exploring lightweight event sourcing 27

“CRUD”

Create/Update/Delete

slide-71
SLIDE 71

Exploring lightweight event sourcing 27

“CRUD”

Create/Update/Delete

Validate input and generate event

HTTP POST

slide-72
SLIDE 72

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”

Create/Update/Delete

Validate input and generate event

HTTP POST

slide-73
SLIDE 73

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

slide-74
SLIDE 74

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”

Create/Update/Delete

Render View

Query

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

slide-75
SLIDE 75

Exploring lightweight event sourcing 27

Event Store

Save Event

“CRUD”

Create/Update/Delete Read

Render View

Query HTTP 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

slide-76
SLIDE 76

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 View Query HTTP GET Validate input and generate event HTTP POST
slide-77
SLIDE 77

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 View Query HTTP 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 }

slide-78
SLIDE 78

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 View Query HTTP 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>

slide-79
SLIDE 79

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 View

Query HTTP GET

Validate input and generate event

HTTP POST

slide-80
SLIDE 80

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 View

Query HTTP GET

Validate input and generate event

HTTP POST

slide-81
SLIDE 81

Exploring lightweight event sourcing

Domain Code

32

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

slide-82
SLIDE 82

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 => // ... } }

slide-83
SLIDE 83

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”

slide-84
SLIDE 84

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”

slide-85
SLIDE 85

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)) } }

slide-86
SLIDE 86

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 ... }

slide-87
SLIDE 87

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

Create/Update/Delete Read

Render View

Query HTTP GET

slide-88
SLIDE 88

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 View

Query HTTP GET

Conflict Resolution

Submit Event Conflicting Events

slide-89
SLIDE 89

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 View

Query HTTP GET

Conflict Resolution

Submit Event Conflicting Events

Checkout revision 23

slide-90
SLIDE 90

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 View

Query HTTP GET

Conflict Resolution

Submit Event Conflicting Events

Checkout revision 23 Submit events based on revision 23

slide-91
SLIDE 91

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 View

Query HTTP GET

Conflict Resolution

Submit Event Conflicting Events

Checkout revision 23 Submit events based on revision 23 Compare submitted events with intermediate events baed on current revision

slide-92
SLIDE 92

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

slide-93
SLIDE 93

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

slide-94
SLIDE 94

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

slide-95
SLIDE 95

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

slide-96
SLIDE 96

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 Solr / Neo4J / HBase / ...

Events

slide-97
SLIDE 97

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 Solr / Neo4J / HBase / ... System Integration Order Fullfillment Payment Provider Legacy System

Events

slide-98
SLIDE 98

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 RDBMS Solr / Neo4J / HBase / ... System Integration Order Fullfillment Payment Provider Legacy System

Events

slide-99
SLIDE 99

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 RDBMS Solr / Neo4J / HBase / ... System Integration Order Fullfillment Payment Provider Legacy System

Events

slide-100
SLIDE 100

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 RDBMS Solr / Neo4J / HBase / ... System Integration Order Fullfillment Payment Provider Legacy System Auditors

Events

slide-101
SLIDE 101

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
slide-102
SLIDE 102

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) } }

slide-103
SLIDE 103

Exploring lightweight event sourcing

Example Application

  • https://github.com/erikrozendaal/scala-

event-sourcing-example

  • (soon) https://github.com/zilverline/lessdb-

example

  • Blog series coming soon at http://

blog.zilverline.com

40

slide-104
SLIDE 104

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

slide-105
SLIDE 105

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

slide-106
SLIDE 106

Exploring lightweight event sourcing

Conclusion

  • Fully capture historical information
  • Independent components where each part
  • f 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

slide-107
SLIDE 107

Thanks!

slide-108
SLIDE 108

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