from functional to reactive
play

From Functional to Reactive patterns in domain modeling Debasish - PowerPoint PPT Presentation

From Functional to Reactive patterns in domain modeling Debasish Ghosh @debasishg Tuesday, 6 October 15 Tuesday, 6 October 15 Domain Modeling Tuesday, 6 October 15 Domain Modeling (Functional) Tuesday, 6 October 15 Domain Modeling


  1. Operation return type - either Parameterized on types a successfully constructed type Module Name or a list of errors trait AccountService[Account, Amount, Balance] { type AccountOp[A] = NonEmptyList[String] \/ A def open(no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType): AccountOp[Account] def close(no: String, closeDate: Option[Date]): AccountOp[Account] def debit(no: String, amount: Amount): AccountOp[Account] def credit(no: String, amount: Amount): AccountOp[Account] //.. } Operations - domain behaviors Tuesday, 6 October 15

  2. Operation return type - either Parameterized on types a successfully constructed type Module Name or a list of errors trait AccountService[Account, Amount, Balance] { type AccountOp[A] = NonEmptyList[String] \/ A def open(no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType): AccountOp[Account] def close(no: String, closeDate: Option[Date]): AccountOp[Account] def debit(no: String, amount: Amount): AccountOp[Account] def credit(no: String, amount: Amount): AccountOp[Account] //.. } explicit & verifiable algebra Operations - domain behaviors Tuesday, 6 October 15

  3. • Parametric - parameterized on types • Statically Typed • Modular and hence unit testable • Composable Tuesday, 6 October 15

  4. Composable def transfer(from: String, to: String, amount: Amount) : AccountOp[(Account, Account)] = for { a <- debit(from, amount) b <- credit(to, amount) } yield ((a, b)) Tuesday, 6 October 15

  5. Composable trait BankingService[Account, Amount, Balance] extends AccountService[Account, Amount, Balance] with InterestPostingService[Account, Amount] with InterestCalculation[Account, Amount] with TaxCalculation[Amount] Tuesday, 6 October 15

  6. trait AccountService[Account, Amount, Balance] { type AccountOp[A] = NonEmptyList[String] \/ A def open( no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType ): AccountRepository => AccountOp[Account] //.. } Tuesday, 6 October 15

  7. trait AccountService[Account, Amount, Balance] { type AccountOp[A] = NonEmptyList[String] \/ A def open( no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType ): AccountRepository => AccountOp[Account] //.. } change the algebra to add functionality Tuesday, 6 October 15

  8. trait AccountService[Account, Amount, Balance] { type Valid[A] = NonEmptyList[String] \/ A type AccountOp[A] = Kleisli[Valid, AccountRepository, A] def open( no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType ): AccountOp[Account] //.. } more algebra, more functionality, more succinct Tuesday, 6 October 15

  9. Being Reactive • Design should not have any contention or central bottlenecks that tend to hamper the progress of the system Tuesday, 6 October 15

  10. Being Reactive • If your domain service publishes APIs that does blocking calls to underlying databases and blocks the central thread of user interaction, you face the specter of unbounded latency Tuesday, 6 October 15

  11. Blocking Kills Tuesday, 6 October 15

  12. Blocking Kills Make your APIs elastic enough so that the perceived response to the user is not affected by the current load on the system Tuesday, 6 October 15

  13. Elasticity (responsive under varying load) Message-driven Resilience (loose coupling, (responsive in the isolation thru face of failures) async message passing) Responsive (through bounded latency) Tuesday, 6 October 15

  14. Being Reactive • Without foregoing the benefits of algebraic reasoning with types Tuesday, 6 October 15

  15. Enter Futures .. Tuesday, 6 October 15

  16. Enter Futures .. • A future is the essence of asynchronous non blocking computation Tuesday, 6 October 15

  17. Enter Futures .. • A future is the essence of asynchronous non blocking computation • Futures compose Tuesday, 6 October 15

  18. Enter Futures .. • A future is the essence of asynchronous non blocking computation • Futures compose • Futures have an algebra Tuesday, 6 October 15

  19. Enter Futures .. • A future is the essence of asynchronous non blocking computation • Futures compose • Futures have an algebra • Organize concurrent code around futures safely and in a compositional way Tuesday, 6 October 15

  20. Goals towards Reactive API • In our use case we would like to augment our domain algebra with future based APIs • Just like an Either or a Kleisli , we would like to have asynchrony as yet another stackable effect within our computation Tuesday, 6 October 15

  21. Stacking of Effects Tuesday, 6 October 15

  22. Stacking of Effects Tuesday, 6 October 15

  23. Monad Transformers type Response[A] = String \/ Option[A] val count: Response[Int] = some(10).right for { maybeCount <- count } yield { for { c <- maybeCount // use c } yield c } Tuesday, 6 October 15

  24. Monad Transformers type Response[A] = String \/ Option[A] val count: Response[Int] = some(10).right for { maybeCount <- count } yield { for { c <- maybeCount // use c } yield c } type Error[A] = String \/ A type Response[A] = OptionT[Error, A] val count: Response[Int] = 10.point[Response] for { c <- count // use c : c is an Int here } yield (()) Tuesday, 6 October 15

  25. Monad Transformers type Response[A] = String \/ Option[A] val count: Response[Int] = some(10).right for { maybeCount <- count } yield { richer algebra for { c <- maybeCount // use c } yield c } type Error[A] = String \/ A type Response[A] = OptionT[Error, A] val count: Response[Int] = 10.point[Response] for{ c <- count // use c : c is an Int here } yield (()) Tuesday, 6 October 15

  26. Monad Transformers • collapses the stack and gives us a single monad to deal with • order of stacking is important though Tuesday, 6 October 15

  27. trait AccountService[Account, Amount, Balance] { type Valid[A] = EitherT[Future, NonEmptyList[String], A] type AccountOp[A] = Kleisli[Valid, AccountRepository, A] def open( no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType ): AccountOp[Account] //.. } Tuesday, 6 October 15

  28. trait AccountService[Account, Amount, Balance] { type Valid[A] = EitherT[Future, NonEmptyList[String], A] type AccountOp[A] = Kleisli[Valid, AccountRepository, A] def open( no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType ): AccountOp[Account] //.. } Tuesday, 6 October 15

  29. Tuesday, 6 October 15

  30. trait AccountService[Account, Amount, Balance] { type Valid[A] = EitherT[Future, NonEmptyList[String], A] type AccountOp[A] = Kleisli[Valid, AccountRepository, A] Reactive .. def open( no: String, name: String, rate: Option[BigDecimal], Algebraically openingDate: Option[Date], accountType: AccountType ): AccountOp[Account] //.. } Tuesday, 6 October 15

  31. class AccountServiceInterpreter extends AccountService[Account, Amount, Balance] { def open(no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType) = kleisli[Valid, AccountRepository, Account] { (repo: AccountRepository) => EitherT { Future { repo.query(no) match { //.. } } } } //.. } Tuesday, 6 October 15

  32. class AccountServiceInterpreter extends AccountService[Account, Amount, Balance] { def open(no: String, name: String, rate: Option[BigDecimal], openingDate: Option[Date], accountType: AccountType) = kleisli[Valid, AccountRepository, Account] { (repo: AccountRepository) => EitherT { Future { repo.query(no) match { normal logic //.. } } } } //.. } Tuesday, 6 October 15

  33. We introduced a whole new effect of asynchrony to implement reactive traits in our domain model API algebra & implementatio n just by composing with another type without any change in the core domain logic. This is the essence of typed functional programming . We have types that model effects functionally and we can just stack them up in the proper order that we need. Tuesday, 6 October 15

  34. Advantages • We are still in the statically typed land even with asynchronous behaviors baked into our APIs • We can reason about our program statically • We can compose asynchronous components to form larger abstractions Tuesday, 6 October 15

  35. Reactive & algebraic patterns in domain modeling for { _ <- open(..) _ <- credit(..) d <- debit(..) } yield d Tuesday, 6 October 15

  36. Reactive & algebraic patterns in domain modeling • Compositional by types for { _ <- open(..) _ <- credit(..) d <- debit(..) } yield d Tuesday, 6 October 15

  37. Reactive & algebraic patterns in domain modeling • Compositional by types for { _ <- open(..) • Individual operations _ <- credit(..) sequential as they thread d <- debit(..) through the comprehension } yield d Tuesday, 6 October 15

  38. Reactive & algebraic patterns in domain modeling • Compositional by types for { _ <- open(..) • Individual operations _ <- credit(..) sequential as they thread d <- debit(..) through the comprehension } yield d • Composed operation doesn’t block the main thread of execution Tuesday, 6 October 15

  39. Reactive & algebraic patterns in domain modeling Tuesday, 6 October 15

  40. Reactive & algebraic patterns in domain modeling trait PortfolioService { type PFOperation[A] = Kleisli[Future, AccountRepository, Seq[A]] def getCurrencyPortfolio(no: String, asOf: Date) : PFOperation[Balance] def getEquityPortfolio(no: String, asOf: Date) : PFOperation[Balance] def getFixedIncomePortfolio(no: String, asOf: Date) : PFOperation[Balance] } Tuesday, 6 October 15

  41. Reactive & algebraic patterns in domain modeling val ccyPF: Future[Seq[Balance]] = getCurrencyPortfolio(accountNo, asOf)(AccountRepository) val eqtPF: Future[Seq[Balance]] = getEquityPortfolio(accountNo, asOf)(AccountRepository) val fixPF: Future[Seq[Balance]] = getFixedIncomePortfolio(accountNo, asOf)(AccountRepository) val portfolio: Future[Portfolio] = for { c <- ccyPF e <- eqtPF f <- fixPF } yield CustomerPortfolio(accountNo, asOf, c ++ e ++ f) Tuesday, 6 October 15

  42. Be Algebraic, as long as you can .. Tuesday, 6 October 15

  43. Beyond Algebra - Reactive Protocols Tuesday, 6 October 15

  44. Conference Badge Printing Reactive Reservations • Ubiquitous language • Ubiquitous language • Entities • Entities • • Value Objects Value Objects • Functions on objects • Functions on objects • Domain Rules • Domain Rules • Schema Protocols • Schema • Operations • Operations Domain Algebra A Domain Algebra C Program Management • Ubiquitous language • Entities • Value Objects • Functions on objects • Domain Rules • Schema • Operations Domain Algebra B Co Co Tuesday, 6 October 15

  45. Conference Badge Printing Reactive Reservations Elasticity (responsive under • Ubiquitous language • Ubiquitous language • Entities • Entities varying load) • • Value Objects Value Objects • Functions on objects • Functions on objects • Domain Rules • Domain Rules • Schema Protocols • Schema • Operations • Operations Message-driven Domain Algebra A Domain Algebra C Resilience (loose coupling, (responsive in the isolation thru face of failures) async message Program passing) Management • Ubiquitous language • Entities • Value Objects • Functions on objects Responsive • Domain Rules • Schema (through bounded • Operations latency) Domain Algebra B Co Co Tuesday, 6 October 15

  46. Asynchronous Messaging Tuesday, 6 October 15

  47. Asynchronous Messaging Tuesday, 6 October 15

  48. Asynchronous Messaging Tuesday, 6 October 15

  49. Asynchronous Messaging Tuesday, 6 October 15

  50. Actors and Domain Models Tuesday, 6 October 15

  51. Actors and Domain Models Powerful Tuesday, 6 October 15

  52. Actors and Domain Models Powerful Un-algebraically Powerful Tuesday, 6 October 15

  53. Actors and Domain Models Powerful Un-algebraically Powerful Gain power at one semantic level but lose the power of reasoning Tuesday, 6 October 15

  54. Using actors indiscriminately throughout your domain model makes algebraic reasoning hard Tuesday, 6 October 15

Recommend


More recommend