Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
@crichardson
Developing functional domain
models with event sourcing
Chris Richardson
Author of POJOs in Action
Founder of...
@crichardson
Presentation goal
How to design functional domain
models using event sourcing?
@crichardson
About Chris
@crichardson
About Chris
Consultant and trainer focusing
on microservices
(http://www.chrisrichardson.net/)
@crichardson
About Chris
Founder of a startup that is creating
a platform that makes it easy for
application developers wr...
@crichardson
For more information
https://github.com/cer/event-sourcing-examples
http://microservices.io
http://plainoldob...
@crichardson
Agenda
Why event sourcing?
Designing a domain model based on event sourcing
Event sourcing and service design
@crichardson
Tomcat
Traditional monolithic architecture
Browser/
Client
WAR/EAR
RDBMS
Customers
Accounts
Transfers
Banking...
@crichardson
But that leads* to
monolithic hell
For large and/or complex applications…
@crichardson
Today: use a microservice, polyglot
architecture
Banking UI
Account Management Service
MoneyTransfer Manageme...
@crichardson
But now we have
distributed data
management problems
@crichardson
Customer management
Maintaining invariants across
service boundaries
Order management
Order Service
placeOrde...
@crichardson
Maintaining invariants with (non-
ACID) NoSQL databases
Non-ACID NoSQL database
Order Service
placeOrder()
Cu...
@crichardson
Use an event-driven
architecture
Services publish events when state changes
Services subscribe to events and ...
@crichardson
Order Management
Order
id : 4567
total: 343
state = CREATED
Customer Management
Customer
creditLimit : 12000
...
@crichardson
How to
atomically
update the database
and
publish events
without 2PC?
(dual write problem)
@crichardson
Update the database
and
Publish events
@crichardson
Event sourcing
For each aggregate (aka. business entity):
Identify (state-changing) domain events
Define Event...
@crichardson
Persists events
NOT current state
Account
balance
open(initial)
debit(amount)
credit(amount)
AccountOpened
Ev...
@crichardson
Replay events to recreate
state
Account
balance
AccountOpenedEvent(balance)
AccountDebitedEvent(amount)
Accou...
@crichardson
The present is a fold over
history
@crichardson
Event
Aggregates: Command =>
Events
AggregateCommand Event
@crichardson
Request handling in an event-sourced application
HTTP
Handler
Event
Store
pastEvents = findEvents(entityId)
Ac...
@crichardson
Event Store publishes events -
consumed by other services
Event
Store
Event
Subscriber
subscribe(EventTypes)
...
@crichardson
Benefits of event sourcing
Solves data consistency issues in a Microservice/NoSQL-based
architecture
Reliable ...
@crichardson
Drawbacks of event sourcing
Weird and unfamiliar
Events = a historical record of your bad design decisions
Ha...
@crichardson
Agenda
Why event sourcing?
Designing a domain model based on event sourcing
Event sourcing and service design
@crichardson
Use the familiar building
blocks of DDD
Entity
Value object
Services
Repositories
Aggregates
@crichardson
Partition the domain model
into Aggregates
Order
OrderLine
Item
quantity
…
Address
street
city
…
Customer
Pro...
Aggregate design
Graph consisting of a root
entity and one or more other
entities and value objects
Each core business ent...
@crichardson
Modular business logic = flexible
deployment
Tomcat
WAR/EAR
Customer
Order
Tomcat
WAR/EAR
Customer
Tomcat
WAR/...
@crichardson
Aggregate granularity is
important
Transaction = processing one command by one aggregate
No opportunity to up...
@crichardson
Aggregate granularity
Customer
Product
Order
Customer
Product
Order
Consistency
Decomposability/
Scalability/...
Designing domain events
Records state changes for an aggregate
Records key “business events”
Part of the public API of the...
@crichardson
Designing commands
Created by a service from incoming request
Processed by an aggregate
Immutable
Contains va...
@crichardson
Hybrid OO/FP domain objects
@crichardson
OO = State + Behavior
creditLimit
creditReservations
Customer
processCommand :
PartialFunction[Command, Event...
@crichardson
Aggregate traits
Map Command to Events
Apply event returning
updated Aggregate
Used by
Event Store
to
reconst...
@crichardson
Customer - command processing
Check
available credit
@crichardson
Customer - applying
events
Immutable
@crichardson
Event Store API
trait EventStore {
def save[T <: Aggregate[T]](entity: T, events: Seq[Event],
assignedId : Op...
@crichardson
Unsatisfying design with
imprecise typing
https://flic.kr/p/2Bh518
“I hate OO”
@crichardson
A detour to Haskell
http://www.meetup.com/oakland-scala/
Oakland Advanced Scala Study Group
https://www.flickr...
@crichardson
Haskell event sourcing
aggregate…
https://gist.github.com/Fristi/7327904
Associated data family
~ abstract ty...
@crichardson
…Haskell Aggregate
@crichardson
Haskell TicTacToe aggregate
Concrete type definitions
Command
Processing
Apply Event
@crichardson
Functional event sourcing in
Scala
@crichardson
“This section briefly explains GHC Haskell’s associated types
and shows in detail how they can be encoded in S...
@crichardson
FP = Separation of State and
Behavior
Customer
creditLimit
…
CustomerAggregate
processCommand(Account, Comman...
@crichardson
Aggregate type classes
Used by
Event Store
to
reconstitute
aggregates
Hardwired
@crichardson
Customer Aggregate….
State
Behavior
@crichardson
…command processing…
@crichardson
… applying events
@crichardson
Using Lenses with aggregates
@crichardson
FP-style Event Store API
Aggregate type class
@crichardson
Agenda
Why event sourcing?
Designing a domain model based on event sourcing
Event sourcing and service design
@crichardson
Event Store
HTTP Request
HTTP Adapter
Event Handler
Cmd
Cmd
Events
Events
Xyz Adapter
Xyz Request
microservic...
@crichardson
Place Order example
As a customer
I want to place an order
So that I get the needed products
Given that my av...
@crichardson
Old-style ACID…
BEGIN TRANSACTION
… RESERVE CREDIT …
… CREATE ORDER…
COMMIT TRANSACTION
… becomes eventually
consistent (BASE)
Updating multiple aggregates
multi-step, event-driven flow
each step updates one
Agg...
@crichardson
Need compensating
transactions
Pre-conditions might be false when attempting to update an
aggregate
Credit ch...
@crichardson
Creating an order
DSL concisely specifies:
1.Creates Customer aggregate
2.Processes command
3.Applies events
4...
@crichardson
Event handling in Account
Durable subscription name
Triggers BeanPostProcessor
1.Load Customer aggregate
2.Pr...
@crichardson
Summary
Event sourcing solves a variety of problems in modern
application architectures
ES-based architecture...
@crichardson
@crichardson chris@chrisrichardson.net
http://plainoldobjects.com http://microservices.io
http://bit.ly/trial...
Upcoming SlideShare
Loading in …5
×

Developing functional domain models with event sourcing (sbtb, sbtb2015)

8,500 views

Published on

Event sourcing persists each entity as a sequence of state changing event. An entity’s current state is derived by replaying the events. Event sourcing is a great way to implement event-driven micro services. When one service updates an entity, the new events are consumed by other services, which then update their own state. In this talk we describe how to implement business logic using a domain model that is based on event sourcing. You will learn how to write functional, immutable domain models in Scala. We will compare and contrast a hybrid OO/FP design with a purely functional approach. You will learn how Domain Driven Design concepts such as bounded contexts and aggregates fit in with event-driven microservices.

Published in: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Developing functional domain models with event sourcing (sbtb, sbtb2015)

  1. 1. @crichardson Developing functional domain models with event sourcing Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com Founder of a microservices platform startup @crichardson chris@chrisrichardson.net http://plainoldobjects.com http://microservices.io
  2. 2. @crichardson Presentation goal How to design functional domain models using event sourcing?
  3. 3. @crichardson About Chris
  4. 4. @crichardson About Chris Consultant and trainer focusing on microservices (http://www.chrisrichardson.net/)
  5. 5. @crichardson About Chris Founder of a startup that is creating a platform that makes it easy for application developers write microservices (http://bit.ly/trialeventuate)
  6. 6. @crichardson For more information https://github.com/cer/event-sourcing-examples http://microservices.io http://plainoldobjects.com/ https://twitter.com/crichardson
  7. 7. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design
  8. 8. @crichardson Tomcat Traditional monolithic architecture Browser/ Client WAR/EAR RDBMS Customers Accounts Transfers Banking UI develop test deploy Simple to Load balancer scale Spring MVC Spring Hibernate ... HTML REST/JSON ACID
  9. 9. @crichardson But that leads* to monolithic hell For large and/or complex applications…
  10. 10. @crichardson Today: use a microservice, polyglot architecture Banking UI Account Management Service MoneyTransfer Management Service Account Database MoneyTransfer Database Standalone services Sharded SQLNoSQL DB
  11. 11. @crichardson But now we have distributed data management problems
  12. 12. @crichardson Customer management Maintaining invariants across service boundaries Order management Order Service placeOrder() Customer Service updateCreditLimit() Customer creditLimit ... has ordersbelongs toOrder total Invariant: sum(open order.total) <= customer.creditLimit ?
  13. 13. @crichardson Maintaining invariants with (non- ACID) NoSQL databases Non-ACID NoSQL database Order Service placeOrder() Customer Service updateCreditLimit() Customer creditLimit ... has ordersbelongs toOrder total Invariant: sum(open order.total) <= customer.creditLimit ?
  14. 14. @crichardson Use an event-driven architecture Services publish events when state changes Services subscribe to events and update their state Maintain eventual consistency across multiple aggregates (in multiple datastores) Synchronize replicated data
  15. 15. @crichardson Order Management Order id : 4567 total: 343 state = CREATED Customer Management Customer creditLimit : 12000 creditReservations: {} Customer creditLimit : 12000 creditReservations: { 4567 -> 343} Order id : 4567 total: 343 state = OPEN Eventually consistent credit checking Message Bus createOrder() Publishes: Subscribes to: Subscribes to: publishes: OrderCreatedEvent CreditReservedEvent OrderCreatedEvent CreditReservedEvent
  16. 16. @crichardson How to atomically update the database and publish events without 2PC? (dual write problem)
  17. 17. @crichardson Update the database and Publish events
  18. 18. @crichardson Event sourcing For each aggregate (aka. business entity): Identify (state-changing) domain events Define Event classes For example, Account: AccountOpenedEvent, AccountDebitedEvent, AccountCreditedEvent ShoppingCart: ItemAddedEvent, ItemRemovedEvent, OrderPlacedEvent
  19. 19. @crichardson Persists events NOT current state Account balance open(initial) debit(amount) credit(amount) AccountOpened Event table AccountCredited AccountDebited 101 450 Account table X 101 101 101 901 902 903 500 250 300
  20. 20. @crichardson Replay events to recreate state Account balance AccountOpenedEvent(balance) AccountDebitedEvent(amount) AccountCreditedEvent(amount) Events Periodically snapshot to avoid loading all events
  21. 21. @crichardson The present is a fold over history
  22. 22. @crichardson Event Aggregates: Command => Events AggregateCommand Event
  23. 23. @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)
  24. 24. @crichardson Event Store publishes events - consumed by other services Event Store Event Subscriber subscribe(EventTypes) publish(event) publish(event) Aggregate CQRS View update() update() Microservice B send notifications …
  25. 25. @crichardson Benefits of event sourcing Solves data consistency issues in a Microservice/NoSQL-based architecture Reliable event publishing: publishes events needed by predictive analytics etc, user notifications,… Eliminates O/R mapping problem (mostly) Reifies state changes: Built-in, reliable audit log, temporal queries Preserved history More easily implement future requirements
  26. 26. @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 => use Command Query Responsibility Segregation (CQRS) to handle queries
  27. 27. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design
  28. 28. @crichardson Use the familiar building blocks of DDD Entity Value object Services Repositories Aggregates
  29. 29. @crichardson Partition the domain model into Aggregates Order OrderLine Item quantity … Address street city … Customer Product name price
  30. 30. Aggregate design Graph consisting of a root entity and one or more other entities and value objects Each core business entity = Aggregate: e.g. customer, Account, Order, Product, …. Reference other aggregate roots via primary key Often contains partial copy of other aggregates’ data Order OrderLine Item quantity productId productName productPrice customerId Address street city …
  31. 31. @crichardson Modular business logic = flexible deployment Tomcat WAR/EAR Customer Order Tomcat WAR/EAR Customer Tomcat WAR/EAR Order OR
  32. 32. @crichardson Aggregate granularity is important Transaction = processing one command by one aggregate No opportunity to update multiple aggregates within a transaction If an update must be atomic (i.e. no compensating transaction) then it must be handled by a single aggregate e.g. scanning boarding pass at security checkpoint or when entering jetway
  33. 33. @crichardson Aggregate granularity Customer Product Order Customer Product Order Consistency Decomposability/ Scalability/ User experience
  34. 34. Designing domain events Records state changes for an aggregate Records key “business events” Part of the public API of the domain model ProductAddedToCart id : TimeUUID productId productName productPrice shoppingCartId Required by aggregate Enrichment: Required by consumers
  35. 35. @crichardson Designing commands Created by a service from incoming request Processed by an aggregate Immutable Contains value objects for Validating request Creating event Auditing user activity
  36. 36. @crichardson Hybrid OO/FP domain objects
  37. 37. @crichardson OO = State + Behavior creditLimit creditReservations Customer processCommand : PartialFunction[Command, Events] applyEvent : PartialFunction[Event, Account] State Behavior
  38. 38. @crichardson Aggregate traits Map Command to Events Apply event returning updated Aggregate Used by Event Store to reconstitute aggregate Event types are not precisely typed :-(
  39. 39. @crichardson Customer - command processing Check available credit
  40. 40. @crichardson Customer - applying events Immutable
  41. 41. @crichardson Event Store API trait 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] }
  42. 42. @crichardson Unsatisfying design with imprecise typing https://flic.kr/p/2Bh518 “I hate OO”
  43. 43. @crichardson A detour to Haskell http://www.meetup.com/oakland-scala/ Oakland Advanced Scala Study Group https://www.flickr.com/photos/georgikeith/2658949664
  44. 44. @crichardson Haskell event sourcing aggregate… https://gist.github.com/Fristi/7327904 Associated data family ~ abstract type members The operations
  45. 45. @crichardson …Haskell Aggregate
  46. 46. @crichardson Haskell TicTacToe aggregate Concrete type definitions Command Processing Apply Event
  47. 47. @crichardson Functional event sourcing in Scala
  48. 48. @crichardson “This section briefly explains GHC Haskell’s associated types and shows in detail how they can be encoded in Scala using type members …”
  49. 49. @crichardson FP = Separation of State and Behavior Customer creditLimit … CustomerAggregate processCommand(Account, Command) : Seq[Events] applyEvent(Account, Event) : Account State Behavior
  50. 50. @crichardson Aggregate type classes Used by Event Store to reconstitute aggregates Hardwired
  51. 51. @crichardson Customer Aggregate…. State Behavior
  52. 52. @crichardson …command processing…
  53. 53. @crichardson … applying events
  54. 54. @crichardson Using Lenses with aggregates
  55. 55. @crichardson FP-style Event Store API Aggregate type class
  56. 56. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design
  57. 57. @crichardson Event Store HTTP Request HTTP Adapter Event Handler Cmd Cmd Events Events Xyz Adapter Xyz Request microservice Aggregate Service Event Adapter
  58. 58. @crichardson Place Order example As a customer I want to place an order So that I get the needed products Given that my available credit is $1500 When I place a $250 order Then the order is created Then my available credit is $1250 Story Scenario Post conditions Pre conditions
  59. 59. @crichardson Old-style ACID… BEGIN TRANSACTION … RESERVE CREDIT … … CREATE ORDER… COMMIT TRANSACTION
  60. 60. … becomes eventually consistent (BASE) Updating multiple aggregates multi-step, event-driven flow each step updates one Aggregate Service creates saga to coordinate workflow A state machine Part of the domain, e.g. Order aggregate OR Synthetic aggregate Post-conditions eventually true Order Customer Created Credit reserved public Order createOrder() { … Creates Order … } Approved
  61. 61. @crichardson Need compensating transactions Pre-conditions might be false when attempting to update an aggregate Credit check might fail => cancel order Credit check succeeded but customer cancels order => undo credit reservation …
  62. 62. @crichardson Creating an order DSL concisely specifies: 1.Creates Customer aggregate 2.Processes command 3.Applies events 4.Persists events
  63. 63. @crichardson Event handling in Account Durable subscription name Triggers BeanPostProcessor 1.Load Customer aggregate 2.Processes command 3.Applies events 4.Persists events
  64. 64. @crichardson Summary Event sourcing solves a variety of problems in modern application architectures ES-based architecture = choice of monolith or microservices Scala is a great language for implementing ES-based domain models: Case classes Pattern matching Recreating state = functional fold over events
  65. 65. @crichardson @crichardson chris@chrisrichardson.net http://plainoldobjects.com http://microservices.io http://bit.ly/trialeventuate

×