Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 topic...
Rich domain
models
State Behavior
Class
• Class models the domain abstraction
• Contains both the state and the
behavior t...
•Algebraic Data Type (ADT) models the
domain abstraction
• Contains only the defining state as
immutable values
• No need t...
Rich domain
models
State Behavior
Class
•We start with the class design
• Make it sufficiently “rich” by putting all
relate...
Lean domain
models
Immutable
State
Behavior
•We start with the functions, the
behaviors of the domain
•We define function a...
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(...
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 oth...
Domain Model Algebra
(algebra of types, functions & laws)
explicit
verifiable
• types
• type constraints
• expression in te...
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
Wedne...
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
Problem Domain
...
market
regulations
tax l...
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
Problem Domain
...
market
regulations
tax l...
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
We...
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
• ...
do trade
process
execution
place
orderProblem Domain
...
behaviors
• Functions
• On Types
• Constraints
Solution Domain
• ...
A Monoid
An algebraic structure
having
• an identity element
• a binary associative
operation
trait Monoid[A] {
def zero: ...
Monoid Laws
An algebraic structure
havingsa
• an identity element
• a binary associative
operation
satisfies
op(x, zero) ==...
A Monoid
• generic
• domain independent
• context unaware
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
Wednesd...
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implici...
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implici...
A Monoid
trait Monoid[A] {
def zero: A
def op(l: A, r: => A): A
}
• generic
• domain independent
• context unaware
implici...
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...
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
c...
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
c...
Bank
Account
Trade
Customer
...
...
...
do trade
process
execution
place
order
...
market
regulations
tax laws
brokerage
c...
.. so we talk about domain algebra, where the
domain entities are implemented with sets of
types and domain behaviors are ...
Functional Modeling encourages Algebraic API Design
which leads to organic evolution of domain models
Wednesday, 23 Septem...
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 S...
Client places order
- flexible format
Transform to internal domain
model entity and place for execution
Trade & Allocate to...
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate:...
def clientOrders: ClientOrderSheet => List[Order]
def execute[Account <: BrokerAccount]: Market => Account
=> Order => Lis...
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate:...
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate:...
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate:...
trait Trading[Account, Trade, ClientOrderSheet, Order,
Execution, Market] {
def clientOrders: ClientOrderSheet => List[Ord...
Algebraic Design
• The algebra is the binding contract of the
API
• Implementation is NOT part of the algebra
• An algebra...
def clientOrders: ClientOrderSheet => List[Order]
def execute: Market => Account => Order => List[Execution]
def allocate:...
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def al...
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def al...
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def al...
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def al...
def clientOrders: ClientOrderSheet => List[Order]
def execute(m: Market, broker: Account): Order => List[Execution]
def al...
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 Sept...
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 ...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
def f[M: Monad]: A => M[B]
def g[M: Monad]: B => M[C]
.. a problem of composition with
effects that can be generalized ..
...
.. 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...
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] =
K...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
def tradeGeneration(
market: Market,
broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(mark...
def tradeGeneration(
market: Market,
broker: Account,
clientAccounts: List[Account]) = {
clientOrders andThen
execute(mark...
algebraic & functional
• Just Pure Functions. Lower cognitive load -
don’t have to think of the classes & data
members whe...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
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 ...
Error handling as an
Effect
• pure and functional
• with an explicit and published algebra
• stackable with existing effec...
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 ...
type Response[A] = String / Option[A]
val count: Response[Int] = some(10).right
for {
maybeCount <- count
} yield {
for {
...
type Response[A] = String / Option[A]
val count: Response[Int] = some(10).right
for {
maybeCount <- count
} yield {
for {
...
type Response[A] = String / Option[A]
val count: Response[Int] = some(10).right
for {
maybeCount <- count
} yield {
for {
...
Monad Transformers
• collapses the stack and gives us a single
monad to deal with
• order of stacking is important though
...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
.. stacking of effects ..
case class ListT[M[_], A] (run: M[List[...
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]...
type StringOr[A] = String / A
type Valid[A] = ListT[StringOr, A]
def clientOrders: Kleisli[Valid, ClientOrderSheet, Order]...
def execute(market: Market, brokerAccount: Account) =
kleisli[List, Order, Execution] { order =>
order.items.map { item =>...
private def makeExecution(brokerAccount: Account,
item: LineItem, market: Market): String / Execution = //..
def execute(m...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[List, Order, Executio...
.. the algebra ..
def clientOrders: Kleisli[List, ClientOrderSheet, Order]
def execute(m: Market, b: Account): Kleisli[Lis...
.. the algebra ..
composition
def tradeGeneration(market: Market, broker: Account,
clientAccounts: List[Account]) = {
clie...
.. the algebra ..
trait OrderLaw {
def sizeLaw: Seq[ClientOrder] => Seq[Order] => Boolean =
{ cos => orders =>
cos.size ==...
Domain Rules as
Algebraic Properties
• part of the abstraction
• equally important as the actual abstraction
• verifiable a...
.. domain rules verification ..
property("Check Client Order laws") =
forAll((cos: Set[ClientOrder]) => {
val orders = for ...
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 Sep...
sealed trait AccountRepoF[+A]
case class Query[+A](no: String, onResult: Account => A) extends AccountRepoF[A]
case class ...
object AccountRepoF {
implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {
def map[A,B](action: Accou...
object AccountRepoF {
implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {
def map[A,B](action: Accou...
lift your algebra into the context
of the free monad ..
trait AccountRepository {
def store(account: Account): AccountRepo...
lift your algebra into the context
of the free monad ..
trait AccountRepository {
def store(account: Account): AccountRepo...
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- ...
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- ...
def open(no: String, name: String, openingDate: Option[Date]) =
for {
_ <- store(Account(no, name, openingDate.get))
a <- ...
Essence of the Pattern
• We have built the entire model of
computation without any semantics, just the
algebra
• Just a de...
Essence of the Pattern
• Now we can provide as many interpreters as
we wish depending on the usage / context
• 1 interpret...
a sample interpreter structure ..
def interpret[A](script: AccountRepo[A], ls: List[String]): List[String] =
script.fold(_...
Intuition ..
• The larger algebra formed from each
individual algebra element is merely a
collection without any interpret...
Takeaways ..
Wednesday, 23 September 15
algebraic design
• evolution based on contracts / types /
interfaces without any dependency on
implementation
Wednesday, 2...
algebraic design
• evolves straight from the domain use cases
using domain vocabulary (ubiquitous
language falls in place ...
algebraic design
• modular and hence pluggable. Each of the
API that we discussed can be plugged off
the specific use case ...
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 thei...
When using functional modeling, always try to express
domain specific abstractions and behaviors in terms of more
generic, ...
Use code “mlghosh2” for a 50% discount
Wednesday, 23 September 15
ThankYou!
Wednesday, 23 September 15
Upcoming SlideShare
Loading in …5
×

Domain Modeling with Functions - an algebraic approach

6,648 views

Published on

Talks about algebraic API design for domain models

Published in: Software
  • Follow the link, new dating source: ❶❶❶ http://bit.ly/39sFWPG ❶❶❶
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Sex in your area is here: ❤❤❤ http://bit.ly/39sFWPG ❤❤❤
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y3nhqquc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @Amp Tanawat try http://www.infoq.com/presentations/functional-ddd-financial
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Domain Modeling with Functions - an algebraic approach

  1. 1. Domain Modeling with Functions an algebraic approach Debasish Ghosh (@debasishg) Wednesday, 23 September 15
  2. 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. 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. 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. 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. 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. 7. The Functional Lens .. “domain API evolution through algebraic composition” Wednesday, 23 September 15
  8. 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
  9. 9. Domain Model Algebra Wednesday, 23 September 15
  10. 10. Domain Model Algebra (algebra of types, functions & laws) Wednesday, 23 September 15
  11. 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. 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
  13. 13. Problem Domain Wednesday, 23 September 15
  14. 14. Bank Account Trade Customer ... ... ... Problem Domain ... entities Wednesday, 23 September 15
  15. 15. Bank Account Trade Customer ... ... ... do trade process execution place order Problem Domain ... entities behaviors Wednesday, 23 September 15
  16. 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. 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. 18. do trade process execution place orderProblem Domain ... behaviors • Functions • On Types • Constraints Solution Domain Wednesday, 23 September 15
  19. 19. do trade process execution place orderProblem Domain ... behaviors • Functions • On Types • Constraints Solution Domain • Morphisms • Sets • Laws Algebra Wednesday, 23 September 15
  20. 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. 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. 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. 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. 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. 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. 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
  27. 27. do trade process execution place order ... Domain Behaviors Wednesday, 23 September 15
  28. 28. Bank Account Trade Customer ... ... ... do trade process execution place order ... Domain Behaviors Domain Types Wednesday, 23 September 15
  29. 29. 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
  30. 30. 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
  31. 31. 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
  32. 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. 33. Functional Modeling encourages Algebraic API Design which leads to organic evolution of domain models Wednesday, 23 September 15
  34. 34. Wednesday, 23 September 15
  35. 35. Client places order - flexible format 1 Wednesday, 23 September 15
  36. 36. Client places order - flexible format Transform to internal domain model entity and place for execution 1 2 Wednesday, 23 September 15
  37. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
  70. 70. more algebra, more types Wednesday, 23 September 15
  71. 71. def clientOrders: Kleisli[List, ClientOrderSheet, Order] return type constructor Wednesday, 23 September 15
  72. 72. def clientOrders: Kleisli[List, ClientOrderSheet, Order] return type constructor What happens in case the operation fails ? Wednesday, 23 September 15
  73. 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. 74. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. M[List[_]] Wednesday, 23 September 15
  75. 75. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. M[List[_]]: M is a Monad Wednesday, 23 September 15
  76. 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. 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. 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. 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. 80. def clientOrders: Kleisli[List, ClientOrderSheet, Order] .. stacking of effects .. case class ListT[M[_], A] (run: M[List[A]]) { //.. Wednesday, 23 September 15
  81. 81. Wednesday, 23 September 15
  82. 82. type StringOr[A] = String / A type Valid[A] = ListT[StringOr, A] Wednesday, 23 September 15
  83. 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. 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. 85. def execute(market: Market, brokerAccount: Account) = kleisli[List, Order, Execution] { order => order.items.map { item => Execution(brokerAccount, market, ..) } } Wednesday, 23 September 15
  86. 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. 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. 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. 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. 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. 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. 92. Domain Rules as Algebraic Properties • part of the abstraction • equally important as the actual abstraction • verifiable as properties Wednesday, 23 September 15
  93. 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
  94. 94. more algebra, more types Wednesday, 23 September 15
  95. 95. a useful pattern for decoupling algebra from implementation Wednesday, 23 September 15
  96. 96. Repository • store domain objects • query domain objects • single point of interface of the domain model Wednesday, 23 September 15
  97. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 109. Takeaways .. Wednesday, 23 September 15
  110. 110. algebraic design • evolution based on contracts / types / interfaces without any dependency on implementation Wednesday, 23 September 15
  111. 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. 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. 113. algebraic design • pure, referentially transparent and hence testable in isolation Wednesday, 23 September 15
  114. 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. 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. 116. Use code “mlghosh2” for a 50% discount Wednesday, 23 September 15
  117. 117. ThankYou! Wednesday, 23 September 15

×