SlideShare ist ein Scribd-Unternehmen logo
1 von 117
Downloaden Sie, um offline zu lesen
Domain Modeling with
Functions
an algebraic approach
Debasish Ghosh
(@debasishg)
Wednesday, 23 September 15
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 specific 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)
Wednesday, 23 September 15
Rich domain
models
State Behavior
Class
• Class models the domain abstraction
• Contains both the state and the
behavior together
• State hidden within private access
specifier for fear of being mutated
inadvertently
• Decision to take - what should go inside
a class ?
• Decision to take - where do we put
behaviors that involve multiple classes ?
Often led to bloated service classes
State Behavior
Wednesday, 23 September 15
•Algebraic Data Type (ADT) models the
domain abstraction
• Contains only the defining state as
immutable values
• No need to make things “private” since
we are talking about immutable values
• Nothing but the bare essential
definitions go inside an ADT
•All behaviors are outside the ADT in
modules as functions that define the
domain behaviors
Lean domain
models
Immutable
State
Behavior
Immutable
State
Behavior
Algebraic Data Types Functions in modules
Wednesday, 23 September 15
Rich domain
models
State Behavior
Class
•We start with the class design
• Make it sufficiently “rich” by putting all
related behaviors within the class, used to
call them fine grained abstractions
•We put larger behaviors in the form of
services (aka managers) and used to call
them coarse grained abstractions
State Behavior
Wednesday, 23 September 15
Lean domain
models
Immutable
State
Behavior
•We start with the functions, the
behaviors of the domain
•We define function algebras using types
that don’t have any implementation yet
(we will see examples shortly)
• Primary focus is on compositionality that
enables building larger functions out of
smaller ones
• Functions reside in modules which also
compose
• Entities are built with algebraic data
types that implement the types we used in
defining the functions
Immutable
State
Behavior
Algebraic Data Types Functions in modules
Wednesday, 23 September 15
The Functional Lens ..
“domain API evolution through algebraic
composition”
Wednesday, 23 September 15
• Set of behaviors
• Can be composed
• Usually closed under composition
• Set of business rules
Domain Model = { f(x) | P(x) Є { domain rules }}
x has a type
Wednesday, 23 September 15
Domain Model Algebra
Wednesday, 23 September 15
Domain Model Algebra
(algebra of types, functions & laws)
Wednesday, 23 September 15
Domain Model Algebra
(algebra of types, functions & laws)
explicit
• types
• type constraints
• expression in terms of other generic algebra
Wednesday, 23 September 15
Domain Model Algebra
(algebra of types, functions & laws)
explicit
verifiable
• types
• type constraints
• expression in terms of other generic algebra
• type constraints
• more constraints if you have DT
• algebraic property based testing
Wednesday, 23 September 15
Problem Domain
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
Problem Domain
...
entities
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
Problem Domain
...
entities
behaviors
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
Problem Domain
...
market
regulations
tax laws
brokerage
commission
rates
...
entities
behaviors
laws
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
Problem Domain
...
market
regulations
tax laws
brokerage
commission
rates
...
entities
behaviors
laws
Wednesday, 23 September 15
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
Wednesday, 23 September 15
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
• Morphisms
• Sets
• Laws
Algebra
Wednesday, 23 September 15
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
• Morphisms
• Sets
• Laws
Algebra
Compose for larger abstractions
Wednesday, 23 September 15
A Monoid
An algebraic structure
having
• an identity element
• a binary associative
operation
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
object MonoidLaws {
def associative[A: Equal: Monoid](a1:
A, a2: A, a3: A): Boolean = //..
def rightIdentity[A: Equal: Monoid](a:
A) = //..
def leftIdentity[A: Equal: Monoid](a:
A) = //..
}
Wednesday, 23 September 15
Monoid Laws
An algebraic structure
havingsa
• an identity element
• a binary associative
operation
satisfies
op(x, zero) == x and op(zero, x) == x
satisfies
op(op(x, y), z) == op(x, op(y, z))
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
object MonoidLaws {
def associative[A: Equal: Monoid](a1:
A, a2: A, a3: A): Boolean = //..
def rightIdentity[A: Equal: Monoid](a:
A) = //..
def leftIdentity[A: Equal: Monoid](a:
A) = //..
}
Wednesday, 23 September 15
A Monoid
• generic
• domain independent
• context unaware
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
Wednesday, 23 September 15
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implicit def MoneyPlusMonoid = new Monoid[Money] {
def zero = //..
def op(m1: Money, m2: Money) = //..
}
• context of the domain
Wednesday, 23 September 15
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implicit def MoneyPlusMonoid = new Monoid[Money] {
def zero = //..
def op(m1: Money, m2: Money) = //..
}
• context of the domain
(algebra)
(interpretation)
Wednesday, 23 September 15
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implicit def MoneyPlusMonoid = new Monoid[Money] {
def zero = //..
def op(m1: Money, m2: Money) = //..
}
• context of the domain
(algebra)
(interpretation)
(reusable across contexts)
(varies with context)
Wednesday, 23 September 15
do trade
process
execution
place
order
...
Domain
Behaviors
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
Domain
Behaviors
Domain Types
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
commission
rates
...
Domain
Behaviors
Domain TypesDomain Rules
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
commission
rates
...
Domain
Behaviors
Domain TypesDomain Rules
Monoid Monad ...
Generic Algebraic
Structures
Wednesday, 23 September 15
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
commission
rates
...
Domain
Behaviors
Domain TypesDomain Rules
Monoid Monad ...
Generic Algebraic
Structures
Domain Algebra
Wednesday, 23 September 15
.. so we talk about domain algebra, where the
domain entities are implemented with sets of
types and domain behaviors are functions that
map a type to one or more types.And
domain rules are the laws which define the
constraints of the business ..
Wednesday, 23 September 15
Functional Modeling encourages Algebraic API Design
which leads to organic evolution of domain models
Wednesday, 23 September 15
Wednesday, 23 September 15
Client places order
- flexible format
1
Wednesday, 23 September 15
Client places order
- flexible format
Transform to internal domain
model entity and place for execution
1 2
Wednesday, 23 September 15
Client places order
- flexible format
Transform to internal domain
model entity and place for execution
Trade & Allocate to
client accounts
1 2
3
Wednesday, 23 September 15
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
Wednesday, 23 September 15
def clientOrders: ClientOrderSheet => List[Order]
def execute[Account <: BrokerAccount]: Market => Account
=> Order => List[Execution]
def allocate[Account <: TradingAccount]: List[Account]
=> Execution => List[Trade]
Wednesday, 23 September 15
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
Wednesday, 23 September 15
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
•Types (domain entities)
• Functions operating on types (domain behaviors)
• Laws (business rules)
Wednesday, 23 September 15
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
•Types (domain entities)
• Functions operating on types (domain behaviors)
• Laws (business rules)
Algebra of the API
Wednesday, 23 September 15
trait Trading[Account, Trade, ClientOrderSheet, Order,
Execution, Market] {
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
def tradeGeneration(market: Market, broker: Account,
clientAccounts: List[Account]) = ???
}
parameterized on typesmodule
Wednesday, 23 September 15
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
Wednesday, 23 September 15
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate: List[Account] => Execution => List[Trade]
let’s do some algebra ..
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
def f: A => List[B]
def g: B => List[C]
def h: C => List[D]
.. a problem of composition ..
Wednesday, 23 September 15
.. a problem of
composition with effects ..
def f: A => List[B]
def g: B => List[C]
def h: C => List[D]
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
Define a mapping
M[B] => B
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
Define a mapping
M[B] => B
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
m.map(f(a))(g)
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
m.map(f(a))(g) M[M[C]]
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
m.join(m.map(f(a))(g)) M[C]
Wednesday, 23 September 15
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
andThen
Wednesday, 23 September 15
.. the glue (combinator) ..
def andThen[M[_], A, B, C](f: A => M[B], g: B => M[C])
(implicit m: Monad[M]): A => M[C] = {(a: A) =>
m.join(m.map(f(a))(g))
}
Wednesday, 23 September 15
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 !
Wednesday, 23 September 15
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]
Follow the types
.. function composition with
Effects ..
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def allocate(accounts: List[Account]): Execution => List[Trade]
Wednesday, 23 September 15
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]
Domain algebra composed with the
categorical algebra of a Kleisli Arrow
.. function composition with
Effects ..
Wednesday, 23 September 15
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]
.. that implements the semantics of our
domain algebraically ..
.. function composition with
Effects ..
Wednesday, 23 September 15
def tradeGeneration(
market: Market,
broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(market, broker) andThen
allocate(clientAccounts)
}
Implementation follows the specification
.. the complete trade generation
logic ..
Wednesday, 23 September 15
def tradeGeneration(
market: Market,
broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(market, broker) andThen
allocate(clientAccounts)
}
Implementation follows the specification
and we get the Ubiquitous Language for
free :-)
.. the complete trade generation
logic ..
Wednesday, 23 September 15
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
defined the algebras of our domain APIs in
terms of existing, time tested algebras of
Kleislis and Monads
Wednesday, 23 September 15
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 handle errors
that may occur within our domain
behaviors ..
.. function composition with
Effects ..
Wednesday, 23 September 15
more algebra,
more types
Wednesday, 23 September 15
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
return type constructor
Wednesday, 23 September 15
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
return type constructor
What happens in case the operation fails ?
Wednesday, 23 September 15
Error handling as an
Effect
• pure and functional
• with an explicit and published algebra
• stackable with existing effects
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
Wednesday, 23 September 15
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
.. stacking of effects ..
M[List[_]]
Wednesday, 23 September 15
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
.. stacking of effects ..
M[List[_]]: M is a Monad
Wednesday, 23 September 15
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
Wednesday, 23 September 15
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
Wednesday, 23 September 15
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
Wednesday, 23 September 15
Monad Transformers
• collapses the stack and gives us a single
monad to deal with
• order of stacking is important though
Wednesday, 23 September 15
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
.. stacking of effects ..
case class ListT[M[_], A] (run: M[List[A]]) { //..
Wednesday, 23 September 15
Wednesday, 23 September 15
type StringOr[A] = String / A
type Valid[A] = ListT[StringOr, A]
Wednesday, 23 September 15
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]
Wednesday, 23 September 15
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 ..
Wednesday, 23 September 15
def execute(market: Market, brokerAccount: Account) =
kleisli[List, Order, Execution] { order =>
order.items.map { item =>
Execution(brokerAccount, market, ..)
}
}
Wednesday, 23 September 15
private def makeExecution(brokerAccount: Account,
item: LineItem, market: Market): String / Execution = //..
def execute(market: Market, brokerAccount: Account) =
kleisli[Valid, Order, Execution] { order =>
listT[StringOr](
order.items.map { item =>
makeExecution(brokerAccount, market, ..)
}.sequenceU
)
}
Wednesday, 23 September 15
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]
.. the algebra ..
Wednesday, 23 September 15
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]
.. the algebra ..
functions
Wednesday, 23 September 15
.. the algebra ..
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]
types
Wednesday, 23 September 15
.. the algebra ..
composition
def tradeGeneration(market: Market, broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(market, broker) andThen
allocate(clientAccounts)
}
Wednesday, 23 September 15
.. the algebra ..
trait OrderLaw {
def sizeLaw: Seq[ClientOrder] => Seq[Order] => Boolean =
{ cos => orders =>
cos.size == orders.size
}
def lineItemLaw: Seq[ClientOrder] => Seq[Order] => Boolean =
{ cos => orders =>
cos.map(instrumentsInClientOrder).sum ==
orders.map(_.items.size).sum
}
}
laws of the algebra
(domain rules)
Wednesday, 23 September 15
Domain Rules as
Algebraic Properties
• part of the abstraction
• equally important as the actual abstraction
• verifiable as properties
Wednesday, 23 September 15
.. domain rules verification ..
property("Check Client Order laws") =
forAll((cos: Set[ClientOrder]) => {
val orders = for {
os <- clientOrders.run(cos.toList)
} yield os
sizeLaw(cos.toSeq)(orders) == true
lineItemLaw(cos.toSeq)(orders) == true
})
property based testing FTW ..
Wednesday, 23 September 15
more algebra,
more types
Wednesday, 23 September 15
a useful pattern for
decoupling algebra from
implementation
Wednesday, 23 September 15
Repository
• store domain objects
• query domain objects
• single point of interface of the domain
model
Wednesday, 23 September 15
sealed trait AccountRepoF[+A]
case class Query[+A](no: String, onResult: Account => A) extends AccountRepoF[A]
case class Store[+A](account: Account, next: A) extends AccountRepoF[A]
case class Delete[+A](no: String, next: A) extends AccountRepoF[A]
algebra of the repository ..
• pure
• compositional
• implementation independent
continuation hole
Wednesday, 23 September 15
object AccountRepoF {
implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {
def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] =
action match {
case Store(account, next) => Store(account, f(next))
case Query(no, onResult) => Query(no, onResult andThen f)
case Delete(no, next) => Delete(no, f(next))
}
}
}
define a functor for the algebra ..
Wednesday, 23 September 15
object AccountRepoF {
implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {
def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] =
action match {
case Store(account, next) => Store(account, f(next))
case Query(no, onResult) => Query(no, onResult andThen f)
case Delete(no, next) => Delete(no, f(next))
}
}
}
define a functor for the algebra ..
you get a free monad ..
type AccountRepo[A] = Free[AccountRepoF, A]
Wednesday, 23 September 15
lift your algebra into the context
of the free monad ..
trait AccountRepository {
def store(account: Account): AccountRepo[Unit] =
liftF(Store(account, ()))
def query(no: String): AccountRepo[Account] =
liftF(Query(no, identity))
def delete(no: String): AccountRepo[Unit] =
liftF(Delete(no, ()))
}
Wednesday, 23 September 15
lift your algebra into the context
of the free monad ..
trait AccountRepository {
def store(account: Account): AccountRepo[Unit] =
liftF(Store(account, ()))
def query(no: String): AccountRepo[Account] =
liftF(Query(no, identity))
def delete(no: String): AccountRepo[Unit] =
liftF(Delete(no, ()))
def update(no: String, f: Account => Account): AccountRepo[Unit] = for {
a <- query(no)
_ <- store(f(a))
} yield ()
}
Wednesday, 23 September 15
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- query(no)
} yield a
build larger abstractions
monadically ..
.. and you get back a free monad
Wednesday, 23 September 15
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- query(no)
} yield a
build larger abstractions
monadically ..
.. and you get back a free monad
.. with 2 operations chained in sequence
Wednesday, 23 September 15
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- query(no)
} yield a
build larger abstractions
monadically ..
.. and you get back a free monad
.. with 2 operations chained in sequence
.. just the algebra, no semantics
Wednesday, 23 September 15
Essence of the Pattern
• We have built the entire model of
computation without any semantics, just the
algebra
• Just a description of what we intend to do
• Not surprising that it’s a pure abstraction
Wednesday, 23 September 15
Essence of the Pattern
• Now we can provide as many interpreters as
we wish depending on the usage / context
• 1 interpreter for testing that tests the
repository actions against an in-memory
data structure
• 1 interpreter for production that uses an
RDBMS
Wednesday, 23 September 15
a sample interpreter structure ..
def interpret[A](script: AccountRepo[A], ls: List[String]): List[String] =
script.fold(_ => ls, {
case Query(no, onResult) =>
interpret(..)
case Store(account, next) =>
interpret(..)
case Delete(no, next) =>
interpret(..)
})
Interpret the whole abstraction and
provide the implementation in context
Wednesday, 23 September 15
Intuition ..
• The larger algebra formed from each
individual algebra element is merely a
collection without any interpretation,
something like an AST
• The interpreter provides the context and
the implementation of each of the algebra
elements under that specific context
Wednesday, 23 September 15
Takeaways ..
Wednesday, 23 September 15
algebraic design
• evolution based on contracts / types /
interfaces without any dependency on
implementation
Wednesday, 23 September 15
algebraic design
• evolves straight from the domain use cases
using domain vocabulary (ubiquitous
language falls in place because of this direct
correspondence)
Wednesday, 23 September 15
algebraic design
• modular and hence pluggable. Each of the
API that we discussed can be plugged off
the specific use case and independently
used in other use cases
Wednesday, 23 September 15
algebraic design
• pure, referentially transparent and hence
testable in isolation
Wednesday, 23 September 15
algebraic design
• compositional, composes with the domain
algebra and with the other categorical
algebras inheriting their semantics
seamlessly into the domain model e.g.
effectful composition with kleislis and fail-
fast error handling with monads
Wednesday, 23 September 15
When using functional modeling, always try to express
domain specific abstractions and behaviors in terms of more
generic, lawful abstractions. Doing this you make your
functions more generic, more usable in a broader context
and yet simpler to comprehend.
Wednesday, 23 September 15
Use code “mlghosh2” for a 50% discount
Wednesday, 23 September 15
ThankYou!
Wednesday, 23 September 15

Weitere ähnliche Inhalte

Ähnlich wie Domain Modeling with Functions - an algebraic approach

Architectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain ModelsArchitectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain ModelsDebasish Ghosh
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingDebasish Ghosh
 
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...apidays
 
Algebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsAlgebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsDebasish Ghosh
 
Shaping serverless architecture with domain driven design patterns - py web-il
Shaping serverless architecture with domain driven design patterns - py web-ilShaping serverless architecture with domain driven design patterns - py web-il
Shaping serverless architecture with domain driven design patterns - py web-ilAsher Sterkin
 
Everything you ever wanted to know about lotus script
Everything you ever wanted to know about lotus scriptEverything you ever wanted to know about lotus script
Everything you ever wanted to know about lotus scriptBill Buchan
 
Shaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsShaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsShimon Tolts
 
Shaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsShaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsAsher Sterkin
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayDebasish Ghosh
 
Cs 1023 lec 10 uml (week 3)
Cs 1023 lec 10 uml (week 3)Cs 1023 lec 10 uml (week 3)
Cs 1023 lec 10 uml (week 3)stanbridge
 
BEAR: Mining Behaviour Models from User-Intensive Web Applications
BEAR: Mining Behaviour Models from User-Intensive Web ApplicationsBEAR: Mining Behaviour Models from User-Intensive Web Applications
BEAR: Mining Behaviour Models from User-Intensive Web ApplicationsGiordano Tamburrelli
 
Code and Design Smells. Are They a Real Threat?
Code and Design Smells. Are They a Real Threat?Code and Design Smells. Are They a Real Threat?
Code and Design Smells. Are They a Real Threat?GlobalLogic Ukraine
 

Ähnlich wie Domain Modeling with Functions - an algebraic approach (20)

Architectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain ModelsArchitectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain Models
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain Modeling
 
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...
apidays London 2023 - Why and how to apply DDD to APIs, Radhouane Jrad, QBE E...
 
Algebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsAlgebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain Models
 
Shaping serverless architecture with domain driven design patterns - py web-il
Shaping serverless architecture with domain driven design patterns - py web-ilShaping serverless architecture with domain driven design patterns - py web-il
Shaping serverless architecture with domain driven design patterns - py web-il
 
Think in linq
Think in linqThink in linq
Think in linq
 
OODIAGRAMS (4).ppt
OODIAGRAMS (4).pptOODIAGRAMS (4).ppt
OODIAGRAMS (4).ppt
 
OODIAGRAMS.ppt
OODIAGRAMS.pptOODIAGRAMS.ppt
OODIAGRAMS.ppt
 
OODIAGRAMS.ppt
OODIAGRAMS.pptOODIAGRAMS.ppt
OODIAGRAMS.ppt
 
Everything you ever wanted to know about lotus script
Everything you ever wanted to know about lotus scriptEverything you ever wanted to know about lotus script
Everything you ever wanted to know about lotus script
 
graph
graphgraph
graph
 
Shaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsShaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patterns
 
Shaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patternsShaping serverless architecture with domain driven design patterns
Shaping serverless architecture with domain driven design patterns
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
 
34. uml
34. uml34. uml
34. uml
 
linq with Exampls
linq with Examplslinq with Exampls
linq with Exampls
 
uml.ppt
uml.pptuml.ppt
uml.ppt
 
Cs 1023 lec 10 uml (week 3)
Cs 1023 lec 10 uml (week 3)Cs 1023 lec 10 uml (week 3)
Cs 1023 lec 10 uml (week 3)
 
BEAR: Mining Behaviour Models from User-Intensive Web Applications
BEAR: Mining Behaviour Models from User-Intensive Web ApplicationsBEAR: Mining Behaviour Models from User-Intensive Web Applications
BEAR: Mining Behaviour Models from User-Intensive Web Applications
 
Code and Design Smells. Are They a Real Threat?
Code and Design Smells. Are They a Real Threat?Code and Design Smells. Are They a Real Threat?
Code and Design Smells. Are They a Real Threat?
 

Kürzlich hochgeladen

Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?Alexandre Beguel
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsJean Silva
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencessuser9e7c64
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolsosttopstonverter
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITmanoharjgpsolutions
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
What’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesWhat’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesVictoriaMetrics
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsChristian Birchler
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxAndreas Kunz
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxRTS corp
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...Bert Jan Schrijver
 

Kürzlich hochgeladen (20)

Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero results
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conference
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration tools
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh IT
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
What’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesWhat’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 Updates
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
 

Domain Modeling with Functions - an algebraic approach

  • 1. Domain Modeling with Functions an algebraic approach Debasish Ghosh (@debasishg) Wednesday, 23 September 15
  • 2. 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 specific 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) Wednesday, 23 September 15
  • 3. Rich domain models State Behavior Class • Class models the domain abstraction • Contains both the state and the behavior together • State hidden within private access specifier for fear of being mutated inadvertently • Decision to take - what should go inside a class ? • Decision to take - where do we put behaviors that involve multiple classes ? Often led to bloated service classes State Behavior Wednesday, 23 September 15
  • 4. •Algebraic Data Type (ADT) models the domain abstraction • Contains only the defining state as immutable values • No need to make things “private” since we are talking about immutable values • Nothing but the bare essential definitions go inside an ADT •All behaviors are outside the ADT in modules as functions that define the domain behaviors Lean domain models Immutable State Behavior Immutable State Behavior Algebraic Data Types Functions in modules Wednesday, 23 September 15
  • 5. Rich domain models State Behavior Class •We start with the class design • Make it sufficiently “rich” by putting all related behaviors within the class, used to call them fine grained abstractions •We put larger behaviors in the form of services (aka managers) and used to call them coarse grained abstractions State Behavior Wednesday, 23 September 15
  • 6. Lean domain models Immutable State Behavior •We start with the functions, the behaviors of the domain •We define function algebras using types that don’t have any implementation yet (we will see examples shortly) • Primary focus is on compositionality that enables building larger functions out of smaller ones • Functions reside in modules which also compose • Entities are built with algebraic data types that implement the types we used in defining the functions Immutable State Behavior Algebraic Data Types Functions in modules Wednesday, 23 September 15
  • 7. The Functional Lens .. “domain API evolution through algebraic composition” Wednesday, 23 September 15
  • 8. • Set of behaviors • Can be composed • Usually closed under composition • Set of business rules Domain Model = { f(x) | P(x) Є { domain rules }} x has a type Wednesday, 23 September 15
  • 10. Domain Model Algebra (algebra of types, functions & laws) Wednesday, 23 September 15
  • 11. Domain Model Algebra (algebra of types, functions & laws) explicit • types • type constraints • expression in terms of other generic algebra Wednesday, 23 September 15
  • 12. Domain Model Algebra (algebra of types, functions & laws) explicit verifiable • types • type constraints • expression in terms of other generic algebra • type constraints • more constraints if you have DT • algebraic property based testing Wednesday, 23 September 15
  • 16. Bank Account Trade Customer ... ... ... do trade process execution place order Problem Domain ... market regulations tax laws brokerage commission rates ... entities behaviors laws Wednesday, 23 September 15
  • 17. Bank Account Trade Customer ... ... ... do trade process execution place order Problem Domain ... market regulations tax laws brokerage commission rates ... entities behaviors laws Wednesday, 23 September 15
  • 18. do trade process execution place orderProblem Domain ... behaviors • Functions • On Types • Constraints Solution Domain Wednesday, 23 September 15
  • 19. do trade process execution place orderProblem Domain ... behaviors • Functions • On Types • Constraints Solution Domain • Morphisms • Sets • Laws Algebra Wednesday, 23 September 15
  • 20. do trade process execution place orderProblem Domain ... behaviors • Functions • On Types • Constraints Solution Domain • Morphisms • Sets • Laws Algebra Compose for larger abstractions Wednesday, 23 September 15
  • 21. A Monoid An algebraic structure having • an identity element • a binary associative operation trait Monoid[A] { def zero: A def op(l: A, r: => A): A } object MonoidLaws { def associative[A: Equal: Monoid](a1: A, a2: A, a3: A): Boolean = //.. def rightIdentity[A: Equal: Monoid](a: A) = //.. def leftIdentity[A: Equal: Monoid](a: A) = //.. } Wednesday, 23 September 15
  • 22. Monoid Laws An algebraic structure havingsa • an identity element • a binary associative operation satisfies op(x, zero) == x and op(zero, x) == x satisfies op(op(x, y), z) == op(x, op(y, z)) trait Monoid[A] { def zero: A def op(l: A, r: => A): A } object MonoidLaws { def associative[A: Equal: Monoid](a1: A, a2: A, a3: A): Boolean = //.. def rightIdentity[A: Equal: Monoid](a: A) = //.. def leftIdentity[A: Equal: Monoid](a: A) = //.. } Wednesday, 23 September 15
  • 23. A Monoid • generic • domain independent • context unaware trait Monoid[A] { def zero: A def op(l: A, r: => A): A } Wednesday, 23 September 15
  • 24. A Monoid trait Monoid[A] { def zero: A def op(l: A, r: => A): A } • generic • domain independent • context unaware implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //.. } • context of the domain Wednesday, 23 September 15
  • 25. A Monoid trait Monoid[A] { def zero: A def op(l: A, r: => A): A } • generic • domain independent • context unaware implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //.. } • context of the domain (algebra) (interpretation) Wednesday, 23 September 15
  • 26. A Monoid trait Monoid[A] { def zero: A def op(l: A, r: => A): A } • generic • domain independent • context unaware implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //.. } • context of the domain (algebra) (interpretation) (reusable across contexts) (varies with context) Wednesday, 23 September 15
  • 32. .. so we talk about domain algebra, where the domain entities are implemented with sets of types and domain behaviors are functions that map a type to one or more types.And domain rules are the laws which define the constraints of the business .. Wednesday, 23 September 15
  • 33. Functional Modeling encourages Algebraic API Design which leads to organic evolution of domain models Wednesday, 23 September 15
  • 35. Client places order - flexible format 1 Wednesday, 23 September 15
  • 36. Client places order - flexible format Transform to internal domain model entity and place for execution 1 2 Wednesday, 23 September 15
  • 37. Client places order - flexible format Transform to internal domain model entity and place for execution Trade & Allocate to client accounts 1 2 3 Wednesday, 23 September 15
  • 38. def clientOrders: ClientOrderSheet => List[Order] def execute: Market => Account => Order => List[Execution] def allocate: List[Account] => Execution => List[Trade] Wednesday, 23 September 15
  • 39. def clientOrders: ClientOrderSheet => List[Order] def execute[Account <: BrokerAccount]: Market => Account => Order => List[Execution] def allocate[Account <: TradingAccount]: List[Account] => Execution => List[Trade] Wednesday, 23 September 15
  • 40. 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 Wednesday, 23 September 15
  • 41. def clientOrders: ClientOrderSheet => List[Order] def execute: Market => Account => Order => List[Execution] def allocate: List[Account] => Execution => List[Trade] •Types (domain entities) • Functions operating on types (domain behaviors) • Laws (business rules) Wednesday, 23 September 15
  • 42. def clientOrders: ClientOrderSheet => List[Order] def execute: Market => Account => Order => List[Execution] def allocate: List[Account] => Execution => List[Trade] •Types (domain entities) • Functions operating on types (domain behaviors) • Laws (business rules) Algebra of the API Wednesday, 23 September 15
  • 43. trait Trading[Account, Trade, ClientOrderSheet, Order, Execution, Market] { def clientOrders: ClientOrderSheet => List[Order] def execute: Market => Account => Order => List[Execution] def allocate: List[Account] => Execution => List[Trade] def tradeGeneration(market: Market, broker: Account, clientAccounts: List[Account]) = ??? } parameterized on typesmodule Wednesday, 23 September 15
  • 44. 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 Wednesday, 23 September 15
  • 45. def clientOrders: ClientOrderSheet => List[Order] def execute: Market => Account => Order => List[Execution] def allocate: List[Account] => Execution => List[Trade] let’s do some algebra .. Wednesday, 23 September 15
  • 46. 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 .. Wednesday, 23 September 15
  • 47. 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 .. Wednesday, 23 September 15
  • 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 .. Wednesday, 23 September 15
  • 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 .. Wednesday, 23 September 15
  • 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 .. Wednesday, 23 September 15
  • 51. def f: A => List[B] def g: B => List[C] def h: C => List[D] .. a problem of composition .. Wednesday, 23 September 15
  • 52. .. a problem of composition with effects .. def f: A => List[B] def g: B => List[C] def h: C => List[D] Wednesday, 23 September 15
  • 53. 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 .. Wednesday, 23 September 15
  • 54. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. Wednesday, 23 September 15
  • 55. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. Define a mapping M[B] => B Wednesday, 23 September 15
  • 56. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. Define a mapping M[B] => B Wednesday, 23 September 15
  • 57. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. m.map(f(a))(g) Wednesday, 23 September 15
  • 58. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. m.map(f(a))(g) M[M[C]] Wednesday, 23 September 15
  • 59. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. m.join(m.map(f(a))(g)) M[C] Wednesday, 23 September 15
  • 60. def f[M: Monad]: A => M[B] def g[M: Monad]: B => M[C] .. a problem of composition with effects that can be generalized .. andThen Wednesday, 23 September 15
  • 61. .. the glue (combinator) .. def andThen[M[_], A, B, C](f: A => M[B], g: B => M[C]) (implicit m: Monad[M]): A => M[C] = {(a: A) => m.join(m.map(f(a))(g)) } Wednesday, 23 September 15
  • 62. 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 ! Wednesday, 23 September 15
  • 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] Follow the types .. function composition with Effects .. def clientOrders: ClientOrderSheet => List[Order] def execute(m: Market, broker: Account): Order => List[Execution] def allocate(accounts: List[Account]): Execution => List[Trade] Wednesday, 23 September 15
  • 64. 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] Domain algebra composed with the categorical algebra of a Kleisli Arrow .. function composition with Effects .. Wednesday, 23 September 15
  • 65. 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] .. that implements the semantics of our domain algebraically .. .. function composition with Effects .. Wednesday, 23 September 15
  • 66. def tradeGeneration( market: Market, broker: Account, clientAccounts: List[Account]) = { clientOrders andThen execute(market, broker) andThen allocate(clientAccounts) } Implementation follows the specification .. the complete trade generation logic .. Wednesday, 23 September 15
  • 67. def tradeGeneration( market: Market, broker: Account, clientAccounts: List[Account]) = { clientOrders andThen execute(market, broker) andThen allocate(clientAccounts) } Implementation follows the specification and we get the Ubiquitous Language for free :-) .. the complete trade generation logic .. Wednesday, 23 September 15
  • 68. 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 defined the algebras of our domain APIs in terms of existing, time tested algebras of Kleislis and Monads Wednesday, 23 September 15
  • 69. 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 handle errors that may occur within our domain behaviors .. .. function composition with Effects .. Wednesday, 23 September 15
  • 71. def clientOrders: Kleisli[List, ClientOrderSheet, Order] return type constructor Wednesday, 23 September 15
  • 72. def clientOrders: Kleisli[List, ClientOrderSheet, Order] return type constructor What happens in case the operation fails ? Wednesday, 23 September 15
  • 73. Error handling as an Effect • pure and functional • with an explicit and published algebra • stackable with existing effects def clientOrders: Kleisli[List, ClientOrderSheet, Order] Wednesday, 23 September 15
  • 74. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. M[List[_]] Wednesday, 23 September 15
  • 75. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. M[List[_]]: M is a Monad Wednesday, 23 September 15
  • 76. 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 Wednesday, 23 September 15
  • 77. 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 Wednesday, 23 September 15
  • 78. 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 Wednesday, 23 September 15
  • 79. Monad Transformers • collapses the stack and gives us a single monad to deal with • order of stacking is important though Wednesday, 23 September 15
  • 80. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. case class ListT[M[_], A] (run: M[List[A]]) { //.. Wednesday, 23 September 15
  • 82. type StringOr[A] = String / A type Valid[A] = ListT[StringOr, A] Wednesday, 23 September 15
  • 83. 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] Wednesday, 23 September 15
  • 84. 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 .. Wednesday, 23 September 15
  • 85. def execute(market: Market, brokerAccount: Account) = kleisli[List, Order, Execution] { order => order.items.map { item => Execution(brokerAccount, market, ..) } } Wednesday, 23 September 15
  • 86. private def makeExecution(brokerAccount: Account, item: LineItem, market: Market): String / Execution = //.. def execute(market: Market, brokerAccount: Account) = kleisli[Valid, Order, Execution] { order => listT[StringOr]( order.items.map { item => makeExecution(brokerAccount, market, ..) }.sequenceU ) } Wednesday, 23 September 15
  • 87. 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] .. the algebra .. Wednesday, 23 September 15
  • 88. 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] .. the algebra .. functions Wednesday, 23 September 15
  • 89. .. the algebra .. 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] types Wednesday, 23 September 15
  • 90. .. the algebra .. composition def tradeGeneration(market: Market, broker: Account, clientAccounts: List[Account]) = { clientOrders andThen execute(market, broker) andThen allocate(clientAccounts) } Wednesday, 23 September 15
  • 91. .. the algebra .. trait OrderLaw { def sizeLaw: Seq[ClientOrder] => Seq[Order] => Boolean = { cos => orders => cos.size == orders.size } def lineItemLaw: Seq[ClientOrder] => Seq[Order] => Boolean = { cos => orders => cos.map(instrumentsInClientOrder).sum == orders.map(_.items.size).sum } } laws of the algebra (domain rules) Wednesday, 23 September 15
  • 92. Domain Rules as Algebraic Properties • part of the abstraction • equally important as the actual abstraction • verifiable as properties Wednesday, 23 September 15
  • 93. .. domain rules verification .. property("Check Client Order laws") = forAll((cos: Set[ClientOrder]) => { val orders = for { os <- clientOrders.run(cos.toList) } yield os sizeLaw(cos.toSeq)(orders) == true lineItemLaw(cos.toSeq)(orders) == true }) property based testing FTW .. Wednesday, 23 September 15
  • 95. a useful pattern for decoupling algebra from implementation Wednesday, 23 September 15
  • 96. Repository • store domain objects • query domain objects • single point of interface of the domain model Wednesday, 23 September 15
  • 97. sealed trait AccountRepoF[+A] case class Query[+A](no: String, onResult: Account => A) extends AccountRepoF[A] case class Store[+A](account: Account, next: A) extends AccountRepoF[A] case class Delete[+A](no: String, next: A) extends AccountRepoF[A] algebra of the repository .. • pure • compositional • implementation independent continuation hole Wednesday, 23 September 15
  • 98. object AccountRepoF { implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] { def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] = action match { case Store(account, next) => Store(account, f(next)) case Query(no, onResult) => Query(no, onResult andThen f) case Delete(no, next) => Delete(no, f(next)) } } } define a functor for the algebra .. Wednesday, 23 September 15
  • 99. object AccountRepoF { implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] { def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] = action match { case Store(account, next) => Store(account, f(next)) case Query(no, onResult) => Query(no, onResult andThen f) case Delete(no, next) => Delete(no, f(next)) } } } define a functor for the algebra .. you get a free monad .. type AccountRepo[A] = Free[AccountRepoF, A] Wednesday, 23 September 15
  • 100. lift your algebra into the context of the free monad .. trait AccountRepository { def store(account: Account): AccountRepo[Unit] = liftF(Store(account, ())) def query(no: String): AccountRepo[Account] = liftF(Query(no, identity)) def delete(no: String): AccountRepo[Unit] = liftF(Delete(no, ())) } Wednesday, 23 September 15
  • 101. lift your algebra into the context of the free monad .. trait AccountRepository { def store(account: Account): AccountRepo[Unit] = liftF(Store(account, ())) def query(no: String): AccountRepo[Account] = liftF(Query(no, identity)) def delete(no: String): AccountRepo[Unit] = liftF(Delete(no, ())) def update(no: String, f: Account => Account): AccountRepo[Unit] = for { a <- query(no) _ <- store(f(a)) } yield () } Wednesday, 23 September 15
  • 102. def open(no: String, name: String, openingDate: Option[Date]) = for { _ <- store(Account(no, name, openingDate.get)) a <- query(no) } yield a build larger abstractions monadically .. .. and you get back a free monad Wednesday, 23 September 15
  • 103. def open(no: String, name: String, openingDate: Option[Date]) = for { _ <- store(Account(no, name, openingDate.get)) a <- query(no) } yield a build larger abstractions monadically .. .. and you get back a free monad .. with 2 operations chained in sequence Wednesday, 23 September 15
  • 104. def open(no: String, name: String, openingDate: Option[Date]) = for { _ <- store(Account(no, name, openingDate.get)) a <- query(no) } yield a build larger abstractions monadically .. .. and you get back a free monad .. with 2 operations chained in sequence .. just the algebra, no semantics Wednesday, 23 September 15
  • 105. Essence of the Pattern • We have built the entire model of computation without any semantics, just the algebra • Just a description of what we intend to do • Not surprising that it’s a pure abstraction Wednesday, 23 September 15
  • 106. Essence of the Pattern • Now we can provide as many interpreters as we wish depending on the usage / context • 1 interpreter for testing that tests the repository actions against an in-memory data structure • 1 interpreter for production that uses an RDBMS Wednesday, 23 September 15
  • 107. a sample interpreter structure .. def interpret[A](script: AccountRepo[A], ls: List[String]): List[String] = script.fold(_ => ls, { case Query(no, onResult) => interpret(..) case Store(account, next) => interpret(..) case Delete(no, next) => interpret(..) }) Interpret the whole abstraction and provide the implementation in context Wednesday, 23 September 15
  • 108. Intuition .. • The larger algebra formed from each individual algebra element is merely a collection without any interpretation, something like an AST • The interpreter provides the context and the implementation of each of the algebra elements under that specific context Wednesday, 23 September 15
  • 109. Takeaways .. Wednesday, 23 September 15
  • 110. algebraic design • evolution based on contracts / types / interfaces without any dependency on implementation Wednesday, 23 September 15
  • 111. algebraic design • evolves straight from the domain use cases using domain vocabulary (ubiquitous language falls in place because of this direct correspondence) Wednesday, 23 September 15
  • 112. algebraic design • modular and hence pluggable. Each of the API that we discussed can be plugged off the specific use case and independently used in other use cases Wednesday, 23 September 15
  • 113. algebraic design • pure, referentially transparent and hence testable in isolation Wednesday, 23 September 15
  • 114. algebraic design • compositional, composes with the domain algebra and with the other categorical algebras inheriting their semantics seamlessly into the domain model e.g. effectful composition with kleislis and fail- fast error handling with monads Wednesday, 23 September 15
  • 115. When using functional modeling, always try to express domain specific abstractions and behaviors in terms of more generic, lawful abstractions. Doing this you make your functions more generic, more usable in a broader context and yet simpler to comprehend. Wednesday, 23 September 15
  • 116. Use code “mlghosh2” for a 50% discount Wednesday, 23 September 15