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

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend