developing functional domain models with event sourcing
play

Developing functional domain models with event sourcing Chris - PowerPoint PPT Presentation

Developing functional domain models with event sourcing Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com http://microservices.io


  1. Developing functional domain models with event sourcing Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com http://microservices.io @crichardson

  2. Presentation goal How to develop functional domain models based on event sourcing @crichardson

  3. About Chris Consultant & Founder of Eventuate.IO @crichardson

  4. For more information http://microservices.io http://github.com/cer/microservices-examples/ https://github.com/cer/event-sourcing-examples http://plainoldobjects.com/ https://twitter.com/crichardson http://eventuate.io/ @crichardson

  5. Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing @crichardson

  6. Traditional monolithic architecture WAR/EAR ACID Spring MVC Banking UI HTML REST/JSON Accounts Browser/ Load RDBMS Client balancer Spring Transfers Hibernate Customers Simple ... develop test Tomcat deploy scale @crichardson

  7. But large and/or complex monolithic applications = Trouble! @crichardson

  8. Using a single RDBMS has its limitations @crichardson

  9. Apply the scale cube Y axis - functional decomposition Scale by Z axis - data partitioning splitting Scale by splitting similar different things things X axis - horizontal duplication @crichardson

  10. Today: use a microservice, polyglot architecture Standalone services Banking UI MoneyTransfer Management Account Management Service Service Account MoneyTransfer Database Database NoSQL DB Sharded SQL @crichardson

  11. But now we have distributed data management problems @crichardson

  12. Example: Money transfer Account Management MoneyTransfer Service Management Service SQL NoSQL MoneyTransfer Account Database Database Money Transfer Account #1 No ACID Account #2 No 2PC @crichardson

  13. 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 @crichardson

  14. Eventually consistent money transfer transferMoney() MoneyTransferService AccountService MoneyTransfer MoneyTransfer MoneyTransfer Account Account Account Account fromAccountId = 101 fromAccountId = 101 fromAccountId = 101 id = 101 id = 101 id = 202 id = 202 toAccountId = 202 toAccountId = 202 toAccountId = 202 balance = 250 balance = 195 balance = 125 balance = 180 amount = 55 amount = 55 amount = 55 state = INITIAL state = DEBITED state = COMPLETED Subscribes to: Publishes: AccountDebitedEvent Subscribes to: publishes: AccountCreditedEvent AccountDebitedEvent MoneyTransferCreatedEvent MoneyTransferCreatedEvent AccountCreditedEvent DebitRecordedEvent DebitRecordedEvent Message Bus @crichardson

  15. How to atomically update the database and publish an event without 2PC? (dual write problem) @crichardson

  16. Event sourcing For each aggregate: Identify (state-changing) domain events Define Event classes For example, Account: AccountOpenedEvent, AccountDebitedEvent, AccountCreditedEvent ShoppingCart: ItemAddedEvent, ItemRemovedEvent, OrderPlacedEvent @crichardson

  17. Persists events X NOT current state Account table 101 450 Account Event table balance 101 901 AccountOpened 500 open(initial) debit(amount) 101 902 AccountCredited 250 credit(amount) 101 903 AccountDebited 300 @crichardson

  18. Replay events to recreate state Events AccountOpenedEvent(balance) AccountDebitedEvent(amount) AccountCreditedEvent(amount) Account balance @crichardson

  19. Two actions that must be atomic Before: update state + publish events Now: persist (and publish) events Single action that can be done atomically @crichardson

  20. Optimizing using snapshots Most aggregates have relatively few events BUT consider a 10-year old Account ⇒ many transactions Therefore, use snapshots: Periodically save snapshot of aggregate state Typically serialize a memento of the aggregate Load latest snapshot + subsequent events @crichardson

  21. Request handling in an event-sourced application Microservice A pastEvents = findEvents(entityId) new() applyEvents(pastEvents) HTTP Event Account Handler Store newEvents = processCmd(SomeCmd) saveEvents(newEvents) (optimistic locking) @crichardson

  22. Event Store publishes events - consumed by other services Microservice B update() subscribe(EventTypes) Aggregate publish(event) Event Event Subscriber Store update() publish(event) NoSQL materialized view @crichardson

  23. About the event store Hybrid database and message broker Retrieve events for an aggregate Append to an aggregates events Subscribe to events Event store implementations: Home-grown/DIY geteventstore.com by Greg Young My event store - bit.ly/trialeventuate @crichardson

  24. 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 @crichardson

  25. 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 @crichardson

  26. 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) @crichardson

  27. Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing @crichardson

  28. Use the familiar building blocks of DDD Entity Value object With some Services differences Repositories Aggregates @crichardson

  29. Partition the domain model into Aggregates @crichardson

  30. Aggregate design Order Graph consisting of a root entity and one or more other entities and value objects customerId Each core business entity = Aggregate: e.g. customer, Account, Order, Product, …. OrderLine Reference other aggregate Address Item roots via primary key street Often contains partial copy quantity city of other aggregates’ data productId … productName productPrice

  31. 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 @crichardson

  32. Aggregate granularity Forum Forum Forum moderator moderator moderator User User User author author author Post Post Post Scalability/ Consistency User experience @crichardson

  33. ES-based Aggregate design class Account { Classic, var balance : Money; mutable def debit(amount : Money) { balance = balance - amount domain model } } Event centric, immutable 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) @crichardson

  34. Designing domain events Record state changes for an aggregate Part of the public API of the domain model ProductAddedToCart Required by id : TimeUUID productId aggregate productName productPrice shoppingCartId Enrichment: Used by consumers

  35. 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 @crichardson

  36. Events and Commands @crichardson

  37. Hybrid OO/FP domain objects @crichardson

  38. OO = State + Behavior Account State balance processCommand(cmd : Command) : Seq[Events] applyEvent(event : Event) : Account Behavior @crichardson

  39. Aggregate traits Used by Event Store to Apply event returning reconstitute updated Aggregate aggregate Map Command to Events @crichardson

  40. Account - command processing Prevent overdraft @crichardson

  41. Account - applying events Immutable @crichardson

  42. 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] } @crichardson

  43. FP-style domain objects @crichardson

Recommend


More recommend