domain modeling with functions - an algebraic approach
TRANSCRIPT
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
(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
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 Monoidtrait 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 Monoidtrait 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 Monoidtrait 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
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
Client places order- flexible format
Transform to internal domainmodel entity and place for execution
1 2
Wednesday, 23 September 15
Client places order- flexible format
Transform to internal domainmodel entity and place for execution
Trade & Allocate toclient 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
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).rightfor { 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).rightfor { maybeCount <- count} yield { for { c <- maybeCount // use c } yield c}
type Error[A] = String \/ Atype 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).rightfor { maybeCount <- count} yield { for { c <- maybeCount // use c } yield c}
type Error[A] = String \/ Atype 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
type StringOr[A] = String \/ Atype 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 \/ Atype 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
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
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.
This is the concept of parametricity and is one of the fundamental building blocks of compositionality in FP.
Wednesday, 23 September 15