4. 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)
5. The Functional Lens ..
ādomain API evolution through algebraic
compositionā
6. The Functional Lens ..
ādomain API evolution through algebraic
compositionā
Building larger domain behaviours
out of smaller ones
7. The Functional Lens ..
ādomain API evolution through algebraic
compositionā
Use composition
of pure functions and types
17. A Bounded Context
ā¢ has a consistent vocabulary
ā¢ a set of domain behaviours modelled as
functions on domain objects
implemented as types
ā¢ each of the behaviours honour a set of
business rules
ā¢ related behaviors grouped as modules
19. Domain Model = āŖ(i) Bounded Context(i)
Bounded Context = { m[T1,T2,..] | T(i) ā Types }
ā¢ a module parameterised
on a set of types
20. Domain Model = āŖ(i) Bounded Context(i)
Bounded Context = { m[T1,T2,..] | T(i) ā Types }
Module = { f(x) | p(x) ā Domain Rules }
ā¢ domain function
ā¢ on an object of type x
ā¢ composes with other functions
ā¢ closed under composition
ā¢ business rules
25. Domain Model Algebra
(algebra of types, functions & laws)
explicit
ā¢ types
ā¢ type constraints
ā¢ expression in terms of other generic algebra
26. Domain Model Algebra
(algebra of types, functions & laws)
explicit veriļ¬able
ā¢ types
ā¢ type constraints
ā¢ expr in terms of other generic algebra
ā¢ type constraints
ā¢ more constraints if you have DT
ā¢ algebraic property based testing
36. Domain Model = āŖ(i) Bounded Context(i)
Bounded Context = { m[T1,T2,..] | T(i) ā Types }
Module = { f(x) | p(x) ā Domain Rules }
ā¢ domain function
ā¢ on an object of type x
ā¢ composes with other functions
ā¢ closed under composition
ā¢ business rules
Domain Algebra
Domain Algebra
42. def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
Types out of thin air No implementation till now
Type names resonate domain language
46. Algebraic Design
ā¢ The algebra is the binding contract of the
API
ā¢ Implementation is NOT part of the
algebra
ā¢ An algebra can have multiple interpreters
(aka implementations)
ā¢ One of the core principles of functional
programming is to decouple the algebra
from the interpreter
47. def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
letās do some algebra ..
48. def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
letās do some algebra ..
49. def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
letās do some algebra ..
50. def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
letās do some algebra ..
51. def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
letās do some algebra ..
52. def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
letās do some algebra ..
53. def f: A => List[B]
def g: B => List[C]
def h: C => List[D]
.. a problem of composition ..
54. .. a problem of composition
with effects ..
def f: A => List[B]
def g: B => List[C]
def h: C => List[D]
55. def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
def h[M: Monad]: C => M[D]
.. a problem of composition with effects
that can be generalized ..
56. case class Kleisli[M[_], A, B](run: A => M[B]) {
def andThen[C](f: B => M[C])
(implicit M: Monad[M]): Kleisli[M, A, C] =
Kleisli((a: A) => M.flatMap(run(a))(f))
}
.. function composition with Effects ..
Itās a Kleisli !
61. def tradeGeneration(
market: Market,
broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(market, broker) andThen
allocate(clientAccounts)
}
Implementation follows the speciļ¬cation and we
get the Ubiquitous Language for free :-)
.. the complete trade generation logic ..
62. algebraic & functional
ā¢ Just Pure Functions. Lower cognitive load -
donāt have to think of the classes & data
members where behaviors will reside
ā¢ Compositional. Algebras compose - we
deļ¬ned the algebras of our domain APIs in
terms of existing, time tested algebras of
Kleislis and Monads
63. def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Execution]
def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]
.. our algebra still doesnāt allow customisable
handling of errors that may occur within our
domain behaviors ..
.. function composition with Effects ..
67. Error handling as an
Effect
ā¢ pure and functional
ā¢ with an explicit and published algebra
ā¢ stackable with existing effects
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
70. type Response[A] = String / Option[A]
val count: Response[Int] = some(10).right
for {
maybeCount <- count
} yield {
for {
c <- maybeCount
// use c
} yield c
}
Monad Transformers
71. 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 (())
Monad Transformers
72. 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 (())
Monad Transformers
richer algebra
74. def clientOrders: Kleisli[List, ClientOrderSheet, Order]
.. stacking of effects ..
case class ListT[M[_], A] (run: M[List[A]]) { //..
75. type StringOr[A] = String / A
type Valid[A] = ListT[StringOr, A]
def clientOrders: Kleisli[Valid, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[Valid, Order, Execution]
def allocate(acts: List[Account]): Kleisli[Valid, Execution, Trade]
.. a small change in algebra, a huge step
for our domain model ..