Developing functional domain models with event sourcing (oakjug, sfscala)
-
Upload
chris-richardson -
Category
Software
-
view
1.181 -
download
0
Transcript of Developing functional domain models with event sourcing (oakjug, sfscala)
![Page 1: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/1.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/2.jpg)
@crichardson
Presentation goal
How to develop functional domain models based on event
sourcing
![Page 3: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/3.jpg)
@crichardson
About Chris
Consultant Startup Founder
![Page 4: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/4.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/5.jpg)
@crichardson
Agenda
Why event-sourcing?
Developing functional domain models with event sourcing
![Page 6: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/6.jpg)
@crichardson
Money Transfer example from 2007
![Page 7: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/7.jpg)
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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/8.jpg)
Mutable domain objects
Entities persisted in RDBMS using
Hibernate
![Page 9: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/9.jpg)
Service invocation = transaction
Transactional using Spring
![Page 10: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/10.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/11.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/12.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/13.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/14.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/15.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/16.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/17.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/18.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/19.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/20.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/21.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/22.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/23.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/24.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/25.jpg)
@crichardson
Agenda
Why event-sourcing?
Developing functional domain models with event sourcing
![Page 26: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/26.jpg)
@crichardson
Familiar building blocks
Entity
Value object
Aggregate: root entity + value objects
![Page 27: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/27.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/28.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/29.jpg)
@crichardson
Hybrid OO/FP domain objects
![Page 30: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/30.jpg)
@crichardson
Aggregate traits
Map Command to Events
Apply event returning updated Aggregate
![Page 31: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/31.jpg)
@crichardson
Account - command processing
Prevent overdraft
![Page 32: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/32.jpg)
@crichardson
Account - applying eventsImmutable
![Page 33: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/33.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/34.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/35.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/36.jpg)
@crichardson
FP-style domain objects
![Page 37: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/37.jpg)
@crichardson
Aggregate type classes/implicits
![Page 38: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/38.jpg)
@crichardson
Functional-style MoneyTransfer Aggregate
State
Behavior
![Page 39: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/39.jpg)
@crichardson
Events and Commands
![Page 40: Developing functional domain models with event sourcing (oakjug, sfscala)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/40.jpg)
@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)](https://reader031.fdocuments.us/reader031/viewer/2022032113/55a521a81a28abbf348b47a8/html5/thumbnails/41.jpg)
@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