+ All Categories
Home > Software > Developing functional domain models with event sourcing (oakjug, sfscala)

Developing functional domain models with event sourcing (oakjug, sfscala)

Date post: 14-Jul-2015
Category:
Upload: chris-richardson
View: 1,180 times
Download: 0 times
Share this document with a friend
Popular Tags:
42
@crichardson Developing functional domain models with event sourcing Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson [email protected] http://plainoldobjects.com http://microservices.io
Transcript
Page 1: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Developing functional domain models with event sourcingChris Richardson

Author of POJOs in Action Founder of the original CloudFoundry.com

@crichardson [email protected] http://plainoldobjects.com http://microservices.io

Page 2: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Presentation goal

How to develop functional domain models based on event

sourcing

Page 3: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

About Chris

Consultant Startup Founder

Page 4: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

For more information

https://github.com/cer/event-sourcing-examples

http://microservices.io

http://plainoldobjects.com/

https://twitter.com/crichardson

Page 5: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Agenda

Why event-sourcing?

Developing functional domain models with event sourcing

Page 6: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Money Transfer example from 2007

Page 7: Developing functional domain models with event sourcing (oakjug, sfscala)

An example domain model

debit(amount)credit(amount)

balance

Account

amountdate

BankingTransaction

BankingTransaction transfer(fromId, toId, amount)

MoneyTransferService

findAccount(id)

AccountRepository

<<interface>>OverdraftPolicy

addTransaction(…)

BankingTransactionRepository

NoOverdraftPolicy

limit

LimitedOverdraft

from

to

State +Behavior

Behavior

Page 8: Developing functional domain models with event sourcing (oakjug, sfscala)

Mutable domain objects

Entities persisted in RDBMS using

Hibernate

Page 9: Developing functional domain models with event sourcing (oakjug, sfscala)

Service invocation = transaction

Transactional using Spring

Page 10: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

But today we apply the scale cube

X axis - horizontal duplication

Z axis

- data

partit

ioning

Y axis - functional

decomposition

Scale b

y split

ting s

imilar

thing

s

Scale by splitting

different things

Page 11: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Applying the scale cube

Y-axis splits/functional decomposition

Application = Set[Microservice] - each with its own database

Monolithic database is functionally decomposed

Z-axis splits/sharding

Accounts partitioned across multiple databases

Page 12: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

AccountMoneyTransfer

Account

Money transfer DB

Account DB1

Account DB2

to

from

How to maintain consistency without 2PC?

Page 13: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

SQL + Text Search engine example

Application

MySQL ElasticSearch

How to maintain consistency without 2PC?

Product #1 Product #1

Page 14: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Use an event-driven architecture

Components (e.g. services) publish events when state changes

Components subscribe to events

Maintains eventual consistency across multiple aggregates (in multiple datastores)

Synchronize replicated data

But how to atomically update state AND publish events without 2PC?

Page 15: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Event sourcingFor each aggregate:

Identify (state-changing) domain events

Define Event classes

For example,

Account: AccountOpenedEvent, AccountDebitedEvent, AccountCreditedEvent

ShoppingCart: ItemAddedEvent, ItemRemovedEvent, OrderPlacedEvent

Page 16: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Persists events NOT current state

Account

balance

open(initial) debit(amount) credit(amount)

AccountOpened

Event table

AccountCredited

AccountDebited

101 450

Account tableX101

101

101

901

902

903

500

250

300

Page 17: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Replay events to recreate state

Account

balance

AccountOpenedEvent(balance) AccountDebitedEvent(amount) AccountCreditedEvent(amount)

Events

Page 18: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Before: update state + publish events

Two actions that must be atomic

Single action that can be done atomically

Now: persist (and publish) events

Page 19: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Request handling in an event-sourced application

HTTP Handler

Event Store

pastEvents = findEvents(entityId)

Account

new()

applyEvents(pastEvents)

newEvents = processCmd(SomeCmd)

saveEvents(newEvents)

Microservice A

(optimistic locking)

Page 20: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Event Store publishes events - consumed by other services

Event Store

Event Subscriber

subscribe(EventTypes)

publish(event)

publish(event)

Aggregate

NoSQL materialized

view

update()

update()

Microservice B

Page 21: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Event store implementations

Home-grown/DIY

geteventstore.com by Greg Young

Talk to me about my project :-)

Page 22: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Business benefits of event sourcing

Built-in, reliable audit log

Enables temporal queries

Publishes events needed by big data/predictive analytics etc.

Preserved history ⇒ More easily implement future requirements

Page 23: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Technical benefits of event sourcing

Solves data consistency issues in a Microservice/NoSQL-based architecture:

Atomically save and publish events

Event subscribers update other aggregates ensuring eventual consistency

Event subscribers update materialized views in SQL and NoSQL databases (more on that later)

Eliminates O/R mapping problem

Page 24: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Drawbacks of event sourcing

Weird and unfamiliar

Events = a historical record of your bad design decisions

Handling duplicate events can be tricky

Application must handle eventually consistent data

Event store only directly supports PK-based lookup (more on that later)

Page 25: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Agenda

Why event-sourcing?

Developing functional domain models with event sourcing

Page 26: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Familiar building blocks

Entity

Value object

Aggregate: root entity + value objects

Page 27: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Aggregate root designclass Account { var balance : Money;

def debit(amount : Money) { balance = balance - amount } }

case class Account(balance : Money) {

def processCommand(cmd : Command) : Seq[Event] = ???

def applyEvent(event : Event) : Account = …

}

case class DebitCommand(amount : Money) case class AccountDebitedEvent(amount : Money)

Classic, mutable

domain model

Event centric, immutable

Page 28: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Designing domain eventsNaming

Past tense to reflect that something occurred

Ideally specific: AccountOpened/Debited/Credited

Sometimes vague: FooUpdated

Event attributes

Id - TimeUUID

Other attributes - from command, required to persist entity

Event enrichment

ProductAddedToCart(productId) vs. ProductAddedCart(productInfo)

Extra data to support event consumers

Page 29: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Hybrid OO/FP domain objects

Page 30: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Aggregate traits

Map Command to Events

Apply event returning updated Aggregate

Page 31: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Account - command processing

Prevent overdraft

Page 32: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Account - applying eventsImmutable

Page 33: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Event Store APItrait EventStore {

def save[T <: Aggregate[T]](entity: T, events: Seq[Event], assignedId : Option[EntityId] = None): Future[EntityWithIdAndVersion[T]]

def update[T <: Aggregate[T]](entityIdAndVersion : EntityIdAndVersion, entity: T, events: Seq[Event]): Future[EntityWithIdAndVersion[T]]

def find[T <: Aggregate[T] : ClassTag](entityId: EntityId) : Future[EntityWithIdAndVersion[T]]

def findOptional[T <: Aggregate[T] : ClassTag](entityId: EntityId) Future[Option[EntityWithIdAndVersion[T]]]

def subscribe(subscriptionId: SubscriptionId): Future[AcknowledgableEventStream] }

Page 34: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

AccountService

DSL concisely specifies: 1.Creates Account aggregate 2.Processes command 3.Applies events 4.Persists events

Page 35: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Event handling

1.Load Account aggregate 2.Processes command 3.Applies events 4.Persists events

Page 36: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

FP-style domain objects

Page 37: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Aggregate type classes/implicits

Page 38: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Functional-style MoneyTransfer Aggregate

State

Behavior

Page 39: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Events and Commands

Page 40: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

FP-style event storeEnables inference of

T, and EV

Tells ES how to instantiate aggregate and apply events

Page 41: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

Summary

Event sourcing solves a variety of problems in modern application architectures

Scala is a great language for implementing domain models:

Case classes

Pattern matching

Recreating state = functional fold over events

Page 42: Developing functional domain models with event sourcing (oakjug, sfscala)

@crichardson

@crichardson [email protected]

http://plainoldobjects.com http://microservices.io


Recommended