Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.

Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.

Successfully reported this slideshow.

Like this presentation? Why not share!

- AI and Machine Learning Demystified... by Carol Smith 2554327 views
- The AI Rush by Jean-Baptiste Dumont 684125 views
- 10 facts about jobs in the future by Pew Research Cent... 392921 views
- 2017 holiday survey: An annual anal... by Deloitte United S... 652832 views
- Harry Surden - Artificial Intellige... by Harry Surden 351465 views
- Inside Google's Numbers in 2017 by Rand Fishkin 904855 views

801 views

Published on

DDD using functional and algebraic techniques (presented at DDD Europe 2018)

Published in:
Software

No Downloads

Total views

801

On SlideShare

0

From Embeds

0

Number of Embeds

48

Shares

0

Downloads

19

Comments

0

Likes

5

No embeds

No notes for slide

- 1. Functional and Algebraic Domain Modeling Debasish Ghosh @debasishg
- 2. Functional and Algebraic Domain Modeling Debasish Ghosh @debasishg
- 3. Functional Programming • programming with pure functions • a function’s output is solely determined by the input (much like mathematical functions) • no assignment, no side-effects •(pure) mapping between values • functions compose • expression-oriented programming
- 4. Functional and Algebraic Domain Modeling Debasish Ghosh @debasishg
- 5. What is an Algebra ? Algebra is the study of algebraic structures In mathematics, and more speciﬁcally in abstract algebra, an algebraic structure is a set (called carrier set or underlying set) with one or more ﬁnitary operations deﬁned on it that satisﬁes a list of axioms -Wikipedia (https://en.wikipedia.org/wiki/Algebraic_structure)
- 6. Set A ϕ : A × A → A fo r (a, b) ∈ A ϕ(a, b) a ϕ b given a binary operation for specific a, b or The Algebra of Sets
- 7. Algebraic Thinking • Thinking and reasoning about code in terms of the data types and the operations they support without considering a bit about the underlying implementations • f: A => B and g: B => C, we should be able to reason that we can compose f and g algebraically to build a larger function h: A => C •algebraic composition
- 8. Functional and Algebraic Domain Modeling Debasish Ghosh @debasishg
- 9. Domain Modeling
- 10. Domain Modeling (Functional)
- 11. What is a domain model ? A domain model in problem solving and software engineering is a conceptual model of all the topics related to a speciﬁc problem. It describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain. It does not describe the solutions to the problem. Wikipedia (http://en.wikipedia.org/wiki/Domain_model)
- 12. https://msdn.microsoft.com/en-us/library/jj591560.aspx
- 13. A Bounded Context • has a consistent vocabulary • a set of domain behaviors modeled as functions on domain objects implemented as types • each of the behaviors honor a set of business rules • related behaviors grouped as modules
- 14. Domain Model = ∪(i) Bounded Context(i) Bounded Context = { m[T1,T2,..] | T(i) ∈ Types } Module = { f(x,y,..) | p(x,y) ∈ Domain Rules } • domain function • on an object of types x, y, .. • composes with other functions • closed under composition • business rules
- 15. • Functions / Morphisms • Types / Sets • Composition • Rules / Laws algebra
- 16. explicit veriﬁable • types • type constraints • functions between types • type constraints • more constraints if you have DT • algebraic property based testing (algebra of types, functions & laws of the solution domain model) Domain Model Algebra
- 17. What is meant by the algebra of a type ? •Nothing •Unit •Boolean •Byte •String
- 18. What is meant by the algebra of a type ? •Nothing -> 0 •Unit -> 1 •Boolean -> 2 •Byte -> 256 •String -> a lot
- 19. What is meant by the algebra of a type ? •(Boolean, Unit) •(Byte, Unit) •(Byte, Boolean) •(Byte, Byte) •(String, String)
- 20. What is meant by the algebra of a type ? •(Boolean, Unit) -> 2x1 = 2 •(Byte, Unit) -> 256x1 = 256 •(Byte, Boolean) -> 256x2 = 512 •(Byte, Byte) -> 256x256 = 65536 •(String, String) -> a lot
- 21. What is meant by the algebra of a type ? • Quiz: Generically, how many inhabitants can we have for a type (a, b)? • Answer: 1 inhabitant for each combination of a’s and b’s (a x b)
- 22. Product Types • Ordered pairs of values one from each type in the order speciﬁed - this and that • Can be generalized to a ﬁnite product indexed by a ﬁnite set of indices
- 23. Product Types in Scala type Point = (Int, Int) val p = (10, 12) case class Account(no: String, name: String, address: String, dateOfOpening: Date, dateOfClosing: Option[Date] )
- 24. What is meant by the algebra of a type ? •Boolean or Unit •Byte or Unit •Byte or Boolean •Byte or Byte •String or String
- 25. What is meant by the algebra of a type ? •Boolean or Unit -> 2+1 = 3 •Byte or Unit -> 256+1 = 257 •Byte or Boolean -> 256+2 = 258 •Byte or Byte -> 256+256 = 512 •String or String -> a lot
- 26. Sum Types • Model data structures involving alternatives - this or that • A tree can have a leaf or an internal node which, is again a tree • In Scala, a sum type is usually referred to as an Algebraic DataType (ADT)
- 27. Sum Types in Scala sealed trait Shape case class Circle(origin: Point, radius: BigDecimal) extends Shape case class Rectangle(diag_1: Point, diag_2: Point) extends Shape
- 28. Sum Types are Expressive • Booleans - true or false • Enumerations - sum types may be used to deﬁne ﬁnite enumeration types, whose values are one of an explicitly speciﬁed ﬁnite set • Optionality - the Option data type in Scala is encoded using a sum type • Disjunction - this or that, the Either data type in Scala • Failure encoding - the Try data type in Scala to indicate that the computation may raise an exception
- 29. sealed trait InstrumentType case object CCY extends InstrumentType case object EQ extends InstrumentType case object FI extends InstrumentType sealed trait Instrument { def instrumentType: InstrumentType } case class Equity(isin: String, name: String, issueDate: Date, faceValue: Amount) extends Instrument { final val instrumentType = EQ } case class FixedIncome(isin: String, name: String, issueDate: Date, maturityDate: Option[Date], nominal: Amount) extends Instrument { final val instrumentType = FI } case class Currency(isin: String) extends Instrument { final val instrumentType = CCY }
- 30. De-structuring with Pattern Matching def process(i: Instrument) = i match { case Equity(isin, _, _, faceValue) => // .. case FixedIncome(isin, _, issueDate, _, nominal) => // .. case Currency(isin) => // .. }
- 31. Exhaustiveness Check
- 32. Sum Types and Domain Models • Models heterogeneity and heterogenous data structures are ubiquitous in a domain model • Allows modeling of expressive domain types in a succinct and secure way - secure by construction • Pattern matching makes encoding domain logic easy and expressive
- 33. – Robert Harper in Practical Foundations of Programming Languages “The absence of sums is the origin of C. A. R. Hoare’s self-described ‘billion dollar mistake,’ the null pointer”
- 34. More algebra of types • Exponentiation - f: A => B has b^a inhabitants • Taylor Series - Recursive Data Types • Derivatives - Zippers • …
- 35. Scaling of the Algebra • Since a function is a mapping from the domain of types to the co-domain of types, we can talk about the algebra of a function • A module is a collection of related functions - we can think of the algebra of a module as the union of the algebras of all functions that it encodes • A domain model (one bounded context) can be loosely thought of as a collection of modules, which gives rise to the connotation of a domain model algebra
- 36. Algebraic Composition • Functions compose based on types, which means .. • Algebras compose • Giving rise to larger algebras / functions, which in turn implies .. • We can construct larger domain behaviors by composing smaller behaviors
- 37. Algebras are Ubiquitous • Generic, parametric and hence usable on an inﬁnite set of data types, including your domain model’s types
- 38. Algebras are Ubiquitous • Generic, parametric and hence usable on an inﬁnite set of data types, including your domain model’s types • Clear separation between the contract (the algebra) and its implementations (interpreters)
- 39. Algebras are Ubiquitous • Generic, parametric and hence usable on an inﬁnite set of data types, including your domain model’s types • Clear separation between the contract (the algebra) and its implementations (interpreters) • Standard vocabulary (like design patterns)
- 40. Algebras are Ubiquitous • Generic, parametric and hence usable on an inﬁnite set of data types, including your domain model’s types • Clear separation between the contract (the algebra) and its implementations (interpreters) • Standard vocabulary (like design patterns) • Existing set of reusable algebras offered by the standard libraries
- 41. Roadmap to a Functional and Algebraic Model 1. Identify domain behaviors 2. Identify the algebras of functions (not implementation) 3. Compose algebras to form larger behaviors - follow the types depending on the semantics of compositionality.We call this behavior a program that models the use case 4. Plug in concrete types to complete the implementation
- 42. Domain Model = ∪(i) Bounded Context(i) Bounded Context = { m[T1,T2,..] | T(i) ∈ Types } Module = { f(x,y,..) | p(x,y) ∈ Domain Rules } • domain function • on an object of types x, y • composes with other functions • closed under composition • business rules
- 43. Domain Model = ∪(i) Bounded Context(i) Bounded Context = { m[T1,T2,..] | T(i) ∈ Types } Module = { f(x,y,..) | p(x,y) ∈ Domain Rules } • domain function • on an object of types x, y • composes with other functions • closed under composition • business rules Domain Algebra Domain Algebra
- 44. Given all the properties of algebra, can we consider algebraic composition to be the basis of designing, implementing and modularizing domain models ?
- 45. Client places order - ﬂexible format 1
- 46. Client places order - ﬂexible format Transform to internal domain model entity and place for execution 1 2
- 47. Client places order - ﬂexible format Transform to internal domain model entity and place for execution Trade & Allocate to client accounts 1 2 3
- 48. def fromClientOrder: ClientOrder => Order def execute(market: Market, brokerAccount: Account) : Order => List[Execution] def allocate(accounts: List[Account]) : List[Execution] => List[Trade] trait Trading { } trait TradeComponent extends Trading with Logging with Auditing algebra of domain behaviors / functions functions aggregate upwards into modules modules aggregate into larger modules
- 49. .. so we have a decent algebra of our module, the names reﬂect the appropriate artifacts from the domain (ubiquitous language), the types are well published and we are quite explicit in what the behaviors do ..
- 50. 1. Compositionality - How do we compose the 3 behaviors that we published to generate trade in the market and allocate to client accounts ? 2. Side-effects - We need to compose them alongside all side-effects that form a core part of all non trivial domain model implementations
- 51. • Error handling ? • throw / catch exceptions is not RT • Partiality ? • partial functions can report runtime exceptions if invoked with unhandled arguments (violates RT) • Reading conﬁguration information from environment ? • may result in code repetition if not properly handled • Logging ? • side-effects Side-effects
- 52. Side-effects • Database writes • Writing to a message queue • Reading from stdin / ﬁles • Interacting with any external resource • Changing state in place
- 53. modularity side-effects don’t compose
- 54. .. the semantics of compositionality .. in the presence of side-effects
- 55. Algebra to the rescue ..
- 56. Algebra to the rescue .. of types
- 57. Abstract side-effects into data type constructors
- 58. Abstract side-effects into data type constructors, which we call Effects ..
- 59. Option[A] Either[A,B] (partiality) (disjunction) List[A] (non-determinism) Reader[E,A] (read from environment aka dependency Injection) Writer[W,A] (logging) State[S,A] (state management) IO[A] (external side-effects) .. and there are many many more ..
- 60. F[A] The answer that the effect computesThe additional stuff modeling the computation
- 61. • The F[_] that we saw is an opaque type - it has no denotation till we give it one • The denotation that we give to F[_] depends on the semantics of compositionality that we would like to have for our domain model behaviors
- 62. def fromClientOrder: ClientOrder => F[Order] def execute(market: Market, brokerAccount: Account) : Order => F[List[Execution]] def allocate(accounts: List[Account]) : List[Execution] => F[List[Trade]] trait Trading[F[_]] { } Effect Type
- 63. • Just the Algebra • No denotation, no concrete type • Explicitly stating that we have eﬀectful functions here def fromClientOrder: ClientOrder => F[Order] def execute(market: Market, brokerAccount: Account) : Order => F[List[Execution]] def allocate(accounts: List[Account]) : List[Execution] => F[List[Trade]] trait Trading[F[_]] { } Effect Type
- 64. • .. we have intentionally kept the algebra open for interpretation .. • .. there are use cases where you would like to have multiple interpreters for the same algebra ..
- 65. The Program def tradeGeneration[M[_]: Monad](T: Trading[M]) = for { order <- T.fromClientOrder(cor) executions <- T.execute(m1, ba, order) trades <- T.allocate(List(ca1, ca2, ca3), executions) } yield trades
- 66. class TradingInterpreter[F[_]] (implicit me: MonadError[F, Throwable]) extends Trading[F] { def fromClientOrder: ClientOrder => F[Order] = makeOrder(_) match { case Left(dv) => me.raiseError(new Exception(dv.message)) case Right(o) => o.pure[F] } def execute(market: Market, brokerAccount: Account) : Order => F[List[Execution]] = ... def allocate(accounts: List[Account]) : List[Execution] => F[List[Trade]] = ... } One Sample Interpreter
- 67. • .. one lesson in modularity - commit to a concrete implementation as late as possible in the design .. • .. we have just indicated that we want a monadic effect - we haven’t committed to any concrete monad type even in the interpreter ..
- 68. The Program def tradeGeneration[M[_]: Monad](T: Trading[M]) = for { order <- T.fromClientOrder(cor) executions <- T.execute(m1, ba, order) trades <- T.allocate(List(ca1, ca2, ca3), executions) } yield trades import cats.effect.IO object TradingComponent extends TradingInterpreter[IO] tradeGeneration(TradingComponent).unsafeRunSync
- 69. The Program def tradeGeneration[M[_]: Monad](T: Trading[M]) = for { order <- T.fromClientOrder(cor) executions <- T.execute(m1, ba, order) trades <- T.allocate(List(ca1, ca2, ca3), executions) } yield trades import monix.eval.Task object TradingComponent extends TradingInterpreter[Task] tradeGeneration(TradingComponent)
- 70. The Program def tradeGenerationLoggable[M[_]: Monad] (T: Trading[M], L: Logging[M]) = for { _ <- L.info("starting order processing") order <- T.fromClientOrder(cor) executions <- T.execute(m1, ba, order) trades <- T.allocate(List(ca1, ca2, ca3), executions) _ <- L.info("allocation done") } yield trades object TradingComponent extends TradingInterpreter[IO] object LoggingComponent extends LoggingInterpreter[IO] tradeGenerationLoggable(TradingComponent, LoggingComponent).unsafeRunSync
- 71. Raise the level of abstraction trait Trading[F[_]] { def fromClientOrder : Kleisli[F, ClientOrder, Order] def execute(market: Market, brokerAccount: Account) : Kleisli[F, Order, List[Execution]] def allocate(accounts: List[Account]) : Kleisli[F, List[Execution], List[Trade]] }
- 72. The Program def tradeGeneration[M[_]: Monad](T: Trading[M]) : Kleisli[M, ClientOrder, List[Trade]] = { T.fromClientOrder andThen T.execute(m1, ba) andThen T.allocate(List(ca1, ca2, ca3)) } object TradingComponent extends TradingInterpreter[IO] val tk = tradeGeneration(TradingComponent) tk(cor).unsafeRunSync
- 73. Effects Side-effects
- 74. - Rob Norris at scale.bythebay.io talk - 2017 (https://www.youtube.com/ watch?v=po3wmq4S15A) “Effects and side-effects are not the same thing. Effects are good, side-effects are bugs.Their lexical similarity is really unfortunate because people often conﬂate the two ideas”
- 75. Takeaways • Algebra scales from that of one single data type to an entire bounded context • Algebras compose enabling composition of domain behaviors • Algebras let you focus on the compositionality without any context of implementation • Statically typed functional programming is programming with algebras
- 76. Takeaways • Abstract early, interpret as late as possible • Abstractions / functions compose only when they are abstract and parametric • Modularity in the presence of side-effects is a challenge • Effects as algebras are pure values that can compose based on laws • Honor the law of using the least powerful abstraction that works
- 77. From the Bible “Name classes and operations to describe their effect and purpose, without reference to the means by which they do what they promise.This relieves the client developer of the need to understand the internals.These names should conform to the UBIQUITOUS LANGUAGE so that team members can quickly infer their meaning. Write a test for a behavior before creating it, to force your thinking into client developer mode.” - Eric Evans (Domain Driven Design) in the chapter on Supple Design, while discussing Intention Revealing Interfaces
- 78. • All names are from the domain vocabulary • Just the algebra describing the promise, no implementation details • Purpose and eﬀect explicit - yes, literally explicit with eﬀects def fromClientOrder: ClientOrder => F[Order] def execute(market: Market, brokerAccount: Account) : Order => F[List[Execution]] def allocate(accounts: List[Account]) : List[Execution] => F[List[Trade]] trait Trading[F[_]] { }
- 79. From the Bible “Place as much of the logic of the program as possible into functions, operations that return results with no observable side- effects.” - Eric Evans (Domain Driven Design) in the chapter on Supple Design, while discussing Side-Eﬀect-Free Functions
- 80. def tradeGeneration[M[_]: Monad](T: Trading[M]) = for { order <- T.fromClientOrder(cor) executions <- T.execute(m1, ba, order) trades <- T.allocate(List(ca1, ca2, ca3), executions) } yield trades • The program tradeGeneration is completely side-eﬀect free. It generates a pure value. • Since the program is pure, you can interpret it in many ways (as we saw earlier). • The side-eﬀects occur only when you submit the program to the run time system. • This is also an example where using algebraic & functional approach we get a clear separation between the building of an abstraction and executing it.
- 81. From the Bible “When it ﬁts, deﬁne an operation whose return type is the same as the type of its argument(s). If the implementer has state that is used in the computation, then the implementer is effectively an argument of the operation, so the argument(s) and return value should be of the same type as the implementer. Such an operation is closed under the set of instances of that type.A closed operation provides a high-level interface without introducing any dependency on other concepts.” - Eric Evans (Domain Driven Design) in the chapter on Supple Design, while discussing Closure of Operations
- 82. trait Semigroup[A] { def combine(x: A, y: A): A } trait Monoid[A] extends Semigroup[A] { def empty: A } • With algebraic modeling, you can encode the closure of operations through the algebra of a Monoid. ★ parametric ★ deﬁne the algebra once, implement it as many times based on the context ★ compositionality at the algebra level • For stateful computation, use the algebra of the State Monad and manipulate state as a Monoid
- 83. Questions?

No public clipboards found for this slide

Be the first to comment