Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Developing Functional Domain Models with Event Sourcing

675 vues

Publié le

Video and slides synchronized, mp3 and slide download available at URL http://bit.ly/1NIH4QB.

Chris Richardson describes how to implement business logic using a domain model that is based on event sourcing. He compares and contrasts a hybrid OO/FP design with a purely functional approach. Chris also shows how Domain Driven Design concepts such as bounded contexts and aggregates fit in with event-driven microservices. Filmed at qconnewyork.com.

Chris Richardson is a developer and architect. He is a Java Champion and the author of POJOs in Action, which describes how to build enterprise Java applications with frameworks such as Spring and Hibernate. Chris was also the founder of the original CloudFoundry.com, an early Java PaaS for Amazon EC2.

Publié dans : Technologie
  • Soyez le premier à commenter

Developing Functional Domain Models with Event Sourcing

  1. 1. @crichardson 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
  2. 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /functional-domain-models
  3. 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  4. 4. @crichardson Presentation goal How to develop functional domain models based on event sourcing
  5. 5. @crichardson About Chris Consultant & Founder of Eventuate.IO
  6. 6. @crichardson 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/
  7. 7. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing
  8. 8. @crichardson Tomcat Traditional monolithic architecture Browser/ Client WAR/EAR RDBMS Customers Accounts Transfers Banking UI develop test deploy Simple Load balancer scale Spring MVC Spring Hibernate ... HTML REST/JSON ACID
  9. 9. @crichardson But large and/or complex monolithic applications = Trouble!
  10. 10. @crichardson Using a single RDBMS has its limitations
  11. 11. @crichardson Apply the scale cube X axis - horizontal duplication Z axis -data partitioning Y axis - functional decomposition Scale by splitting sim ilar things Scale by splitting different things
  12. 12. @crichardson Today: use a microservice, polyglot architecture Banking UI Account Management Service MoneyTransfer Management Service Account Database MoneyTransfer Database Standalone services Sharded SQLNoSQL DB
  13. 13. @crichardson But now we have distributed data management problems
  14. 14. @crichardson Example: Money transfer Account Management Service MoneyTransfer Management Service Account Database MoneyTransfer Database Account #1 Money Transfer Account #2 No 2PC No ACID NoSQL SQL
  15. 15. @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
  16. 16. @crichardson MoneyTransferService MoneyTransfer fromAccountId = 101 toAccountId = 202 amount = 55 state = INITIAL MoneyTransfer fromAccountId = 101 toAccountId = 202 amount = 55 state = DEBITED MoneyTransfer fromAccountId = 101 toAccountId = 202 amount = 55 state = COMPLETED Eventually consistent money transfer Message Bus AccountService transferMoney() Publishes: Subscribes to: Subscribes to: publishes: MoneyTransferCreatedEvent AccountDebitedEvent DebitRecordedEvent AccountCreditedEvent MoneyTransferCreatedEvent DebitRecordedEvent AccountDebitedEvent AccountCreditedEvent Account id = 101 balance = 250 Account id = 202 balance = 125 Account id = 101 balance = 195 Account id = 202 balance = 180
  17. 17. @crichardson How to atomically update the database and publish an event without 2PC? (dual write problem)
  18. 18. @crichardson Event sourcing For each aggregate: 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
  21. 21. @crichardson Before: update state + publish events Two actions that must be atomic Single action that can be done atomically Now: persist (and publish) events
  22. 22. @crichardson 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
  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 NoSQL materialized view update() update() Microservice B
  25. 25. @crichardson 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
  26. 26. @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
  27. 27. @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
  28. 28. @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)
  29. 29. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing
  30. 30. @crichardson Use the familiar building blocks of DDD Entity Value object Services Repositories Aggregates With some differences
  31. 31. @crichardson Partition the domain model into Aggregates
  32. 32. 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 …
  33. 33. @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
  34. 34. @crichardson Aggregate granularity Forum Post User moderator author Forum Post User moderator author Forum Post User moderator author Consistency Scalability/ User experience
  35. 35. @crichardson ES-based Aggregate design class 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
  36. 36. Designing domain events Record state changes for an aggregate Part of the public API of the domain model ProductAddedToCart id : TimeUUID productId productName productPrice shoppingCartId Required by aggregate Enrichment: Used by consumers
  37. 37. @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
  38. 38. @crichardson Events and Commands
  39. 39. @crichardson Hybrid OO/FP domain objects
  40. 40. @crichardson OO = State + Behavior balance Account processCommand(cmd : Command) : Seq[Events] applyEvent(event : Event) : Account State Behavior
  41. 41. @crichardson Aggregate traits Map Command to Events Apply event returning updated Aggregate Used by Event Store to reconstitute aggregate
  42. 42. @crichardson Account - command processing Prevent overdraft
  43. 43. @crichardson Account - applying events Immutable
  44. 44. @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] }
  45. 45. @crichardson FP-style domain objects
  46. 46. @crichardson FP = Separation of State and Behavior Account balance AccountAggregate processCommand(Account, Command) : Seq[Events] applyEvent(Account, Event) : Account State Behavior
  47. 47. @crichardson Aggregate type classes/implicits Used by Event Store to reconstitute aggregate
  48. 48. @crichardson Functional-style Account Aggregate State Behavior
  49. 49. @crichardson Functional-style Account Aggregate Behavior
  50. 50. @crichardson FP-style event store Enables inference of T, and EV Tells ES how to instantiate aggregate and apply events = Strategy
  51. 51. @crichardson Haskell aggregate https://gist.github.com/Fristi/7327904
  52. 52. @crichardson Haskell TicTacToe aggregate
  53. 53. @crichardson JavaScript aggregate
  54. 54. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing
  55. 55. @crichardson Designing services Responsibilities and collaborations Invoked by adapter, e.g. HTTP controller Creates a Command Selects new aggregate or existing aggregate to process command Load aggregate from same bounded context, e.g. Add a Post to a Forum - load forum Load data from another other bounded context, e.g. addProductToCart() Requests ProductInfo from ProductService Invokes PricingService to calculate discount price Sometimes loads target aggregate before creating command e.g. addProductToCart() needs contents of shopping cart to calculate discounted price of product to add
  56. 56. @crichardson Money transfer example As a customer of the bank I want to transfer money between two bank accounts So that I don't have to write a check Given that my open savings account balance is $150 Given that my open checking account balance is $10 When I transfer $50 from my savings account to my checking account Then my savings account balance is $100 Then my checking account balance is $60 Then a MoneyTransfer was created Story Scenario Post conditions Pre conditions
  57. 57. @crichardson Old-style ACID… public class MoneyTransferServiceImpl …{ private final AccountRepository accountRepository; private final MoneyTransferRepository moneyTransferRepository; … @Transactional public MoneyTransfer transfer( String fromAccountId, String toAccountId, double amount) throws MoneyTransferException { Account fromAccount = accountRepository.findAccount(fromAccountId); Account toAccount = accountRepository.findAccount(toAccountId); // … Verify accounts are open … fromAccount.debit(amount); toAccount.credit(amount); return moneyTransferRepository.createMoneyTransfer( fromAccount, toAccount, amount); } }
  58. 58. … 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. MoneyTransfer aggregate OR Synthetic aggregate Post-conditions eventually true Money
 Transfer From Account To Account created debited debit recorded credited public MoneyTransfer transfer() { … Creates MoneyTransfer … }
  59. 59. @crichardson Need compensating transactions Pre-conditions might be false when attempting to update an aggregate For example: an account might be closed transferring money: from account when debiting stop transfer to account reverse the debit from account when attempting reversal bank wins!
  60. 60. @crichardson MoneyTransferService Remoting proxy DSL concisely specifies: 1.Creates Account aggregate 2.Processes command 3.Applies events 4.Persists events
  61. 61. @crichardson Event handling in Account 1.Load Account aggregate 2.Processes command 3.Applies events 4.Persists events Durable subscription nameTriggers BeanPostProcessor
  62. 62. @crichardson JavaScript service
  63. 63. @crichardson Agenda Why event sourcing? Designing a domain model based on event sourcing Event sourcing and service design Microservices and event sourcing
  64. 64. @crichardson Event Store only supports PK- based lookup Therefore….
  65. 65. @crichardson ES+CQRS-based microservices architecture Event Store Command-side Updates Events Query-side Queries (Denormalized) View Events Aggregate
  66. 66. @crichardson Modular domain model Forum Post User moderator author Forum Post User moderator author Tightly coupled ACID Loosely coupled aggregates Eventually consistent
  67. 67. @crichardson MonolithicFirst approach Tomcat WAR/EAR Forum Post User moderator author Not entirely free though - Event Sourcing premium
  68. 68. @crichardson But no Big Ball of Mud to untangle
  69. 69. @crichardson Microservices deployment Tomcat WAR/EAR Much higher - microservices premium Forum Tomcat WAR/EAR Post Tomcat WAR/EAR User
  70. 70. @crichardson Summary Event sourcing solves a variety of problems in modern application architectures Scala is a great language for implementing ES-based domain models: Case classes Pattern matching Recreating state = functional fold over events But Java, JavaScript and Haskell work too! ES-based architecture = flexible deployment
  71. 71. @crichardson @crichardson chris@chrisrichardson.net http://plainoldobjects.com http://microservices.io
  72. 72. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations/functional -domain-models