SlideShare ist ein Scribd-Unternehmen logo
1 von 88
Downloaden Sie, um offline zu lesen
Domain Modeling in a
                     Functional World
                        some real life experiences




Monday 18 June 12
Debasish Ghosh

                          @debasishg on Twitter

                                         code @
                      http://github.com/debasishg

                                           blog @
                    Ruminations of a Programmer
                    http://debasishg.blogspot.com




Monday 18 June 12
What is Domain
                           Modeling
                    • We limit ourselves strictly to how the
                      domain behaves internally and how it
                      responds to events that it receives from
                      the external context
                    • We think of a domain model as being
                      comprised of the core granular
                      abstractions that handle the business logic
                      and a set of coarser level services that
                      interacts with the external world

Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
Functional domain
                            models
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                    Event Sourcing
                    A declarative design for domain service layer


Monday 18 June 12
Immutability

                    • Immutable data
                     ✦   can be shared freely
                     ✦   no shared mutable state and hence no
                         locks, semaphores etc.
                     ✦   and you get some parallelism free



Monday 18 June 12
Immutability
                    • Algebraic Data Types for representation of
                      domain entities
                    • Immutable structures like type-lenses for
                      functional updates
                    • No in-place mutation
                    • Heavy use of persistent data structures

Monday 18 June 12
Algebraic Data Types

                    Ok .. I get the Data Type part, but give me
                                  the ALGEBRA ...




Monday 18 June 12
Algebraic Data Types
               • For simplicity let’s assume that an algebraic
                    data type induces an algebra which gives us
                    some structures and some functions (or
                    morphisms or arrows) to manipulate. So we
                    have some types and some functions that morph
                    one type into another
               • Well .. almost .. it’s actually an initial algebra,
                    but let’s stop at that without sounding more
                    scary

Monday 18 June 12
Rather we look at
                          some examples ..
                    •   Unit represented by 1

                    •   Optional data type represented as Option in
                        Scala (Maybe in Haskell) - a Sum type 1 + X

                    •   Tuples represented as pairs (a, b) - the simplest
                        Product type
                    •   Recursive data types - Lists of X represented by
                        L=1+X*L
                    •   Binary Trees, represented as B = 1 + X * B2


Monday 18 June 12
A Taste of Algebra
                                                                        unit type

           •        A List[Int] can be either an empty list or
                    consisting of one integer, or two integers, or three etc.

                                        sum type
                                                         product type

           •        So a list of integers can be represented algebraically
                    as
                    1 + int + int * int + int * int * int + ... OR
                    1 + int + int ** 2 + int ** 3 + ..


Monday 18 June 12
and that’s not all ..


                    • we can have Taylor series expansion,
                      Composition, and even Differentiation on
                      types ..




Monday 18 June 12
and that’s not all ..
                                               een
                                           llow
                                         Ha
                                    r a
                                 fo
                             em ion
                          th series expansion,
          • we can have e cuss
                        Taylor
                     rv and even Differentiation on
                  se d
            Composition, is
               re..
          can
            types
       we



Monday 18 June 12
Product Type
                    Formal Definition from Bob Harper PFPL
                    Chapter 14 :
                    “The binary product of two types consists of
                    ordered pairs of values, one from each type in the
                    order specified. The associated eliminatory forms
                    are projections, which select the first and second
                    component of a pair. The nullary product, or unit
                    type consists solely of the unique null tuple of no
                    values, and has no associated eliminatory form”

Monday 18 June 12
Product Type
               • Implemented through case classes in Scala
               • In a domain model we represent entities
                    using product types

               • A tuple is the simplest product type :
                    val point = (x_cord, y_cord)




Monday 18 June 12
Representation as
                    ADTs (product type)
    case class Trade(account: Account, instrument: Instrument,
      refNo: String, market: Market,
      unitPrice: BigDecimal, quantity: BigDecimal,
      tradeDate: Date = Calendar.getInstance.getTime,
      valueDate: Option[Date] = None,
      taxFees: Option[List[(TaxFeeId, BigDecimal)]] = None,
      netAmount: Option[BigDecimal] = None) {

         override def equals(that: Any) =
           refNo == that.asInstanceOf[Trade].refNo
         override def hashCode = refNo.hashCode

    }




Monday 18 June 12
Sum Type
                    Formally from Bob Harper in PFPL, Chapter 15 :
                    “Most data structures involve alternatives such as the
                    distinction between a leaf and an interior node in a tree,
                    or a choice in the outermost form of a piece of abstract
                    syntax. Importantly, the choice determines the structure
                    of the value. For example, nodes have children, but leaves
                    do not, and so forth. These concepts are expressed by
                    sum types, specifically the binary sum, which offers a
                    choice of two things, and the nullary sum, which offers a
                    choice of no things”


Monday 18 June 12
Sum Type
              • Implemented through subtyping in Scala
              • Option is one of the most commonly used
                    sum type
                    sealed abstract class Option[+A] extends Product with
                    Serializable //..

                    final case class Some[+A](x: A) extends
                    Option[A] //..

                    case object None extends Option[Nothing] //..




Monday 18 June 12
Representation as
                    ADTs (sum type)
        // various tax/fees to be paid when u do a trade
        sealed trait TaxFeeId extends Serializable
        case object TradeTax extends TaxFeeId
        case object Commission extends TaxFeeId
        case object VAT extends TaxFeeId




Monday 18 June 12
ADTs & Domain
                               Modeling
                    •   Encouraging immutability
                        •   In Scala you can use vars to have mutable case
                            classes, but that’s not encouraged
                        •   With Haskell algebraic data types are
                            immutable values and you can use things like
                            the State monad for implicit state update
                        •   There are some specialized data structures that
                            allow functional updates e.g. Lens, Zipper etc.



Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
Updating a Domain
                     Structure functionally
                    • A Type-Lens is a data structure that sets up
                      a bidirectional transformation between a
                      set of source structures S and target
                      structures T
                    • A Type-Lens is set up as a pair of functions:
                      •   get S -> T
                      •   putBack (S X T) -> S


Monday 18 June 12
Type Lens in Scala

                    case class Lens[A, B] (
                      get: A => B,
                      set: (A, B) => A
                    ) //..




Monday 18 June 12
A Type Lens in Scala
             // change ref no
             val refNoLens: Lens[Trade, String] =
               Lens((t: Trade) => t.refNo,
                       (t: Trade, r: String) => t.copy(refNo = r))



              a function that takes a
        trade and returns it’s reference no        a function that updates a
                                              trade with a supplied reference no




Monday 18 June 12
Lens under Composition
                    • What’s the big deal with a Type Lens ?
                       ✦   Lens compose and hence gives you a cool
                           syntax to update nested structures within
                           an ADT

                    def addressL: Lens[Person, Address] = ...
                    def streetL: Lens[Address, String] = ...
                    val personStreetL: Lens[Person, String] =
                      streetL compose addressL




Monday 18 June 12
Lens under
                      composition
    Using the personStreetL lens we may access or
    set the (indirect) street property of a Person instance

                    val str: String =
                      personStreetL get person

                    val newP: Person =
                      personStreetL set (person, "Bob_St")




Monday 18 June 12
Functional updates in our
                    domain model using Type Lens
           // change ref no
           val refNoLens: Lens[Trade, String] =
             Lens((t: Trade) => t.refNo,
                  (t: Trade, r: String) => t.copy(refNo = r))

           // add tax/fees
           val taxFeeLens: Lens[Trade, Option[List[(TaxFeeId, BigDecimal)]]] =
             Lens((t: Trade) => t.taxFees,
                  (t: Trade, tfs: Option[List[(TaxFeeId, BigDecimal)]]) =>
           t.copy(taxFees = tfs))

           // add net amount
           val netAmountLens: Lens[Trade, Option[BigDecimal]] =
             Lens((t: Trade) => t.netAmount,
                  (t: Trade, n: Option[BigDecimal]) => t.copy(netAmount = n))

           // add value date
           val valueDateLens: Lens[Trade, Option[Date]] =
             Lens((t: Trade) => t.valueDate,
                  (t: Trade, d: Option[Date]) => t.copy(valueDate = d))




Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
Type class
                    • Ad hoc polymorphism
                    • Open, unlike subtyping - makes more sense
                      in domain modeling because domain rules
                      also change. Useful for designing open APIs
                      which can be adapted later to newer types
                    • Leads to generic, modular and reusable
                      code


Monday 18 June 12
Useful type classes behind
                 your domain model
               • Functor - offers you the capability to map
                    over a structure. And not surprisingly, it has a
                    single method: map
                    trait Functor[F[_]] {

                        def map[A, B](fa: F[A])(f: A => B): F[B]

                        //..

                    }




Monday 18 June 12
More type classes
               •    Applicative Functors - Beefed up functors. Here
                    the function is wrapped within a functor. So we
                    lift a function wrapped in a functor to apply on
                    another functor
                    trait Applicative[F[_]] extends Functor {

                        def apply[A, B](f: F[A => B]): F[A] => F[B]

                        def pure[A](a: A): F[A]

                        //..

                    }




Monday 18 June 12
More type classes
               •    Monads - beefed up Applicative Functor, where in
                    addition to apply and map, you get a bind
                    (>>=) function which helps you bind a monadic
                    value to a function’s input that produces a
                    monadic output

                    trait Monad[F[_]] extends Applicative {

                        def >>=[A, B] (fa: F[A])(f: A => F[B]): F[B]

                    }




Monday 18 June 12
And some more ..
               •    Semigroup - a type class that offers an associative binary
                    operation
                    trait Semigroup[F] {

                        def append(f1: F, f2: => F): F

                        //..

                    }


               •    Monoid - a Semigroup with an identity element
                    trait Monoid[F] extends Semigroup[F] {

                        def zero: F

                        //..

                    }




Monday 18 June 12
Accumulating validation
               errors using Type Classes

                    • We can use Semigroup to accumulate
                      errors in our domain validation logic
                    • Semigroup is an abstraction that offers an
                      associative binary append operation -
                      looks intuitive for our use case




Monday 18 June 12
Accumulating validation
               errors using Type Classes
                    sealed trait Validation[+e, +A] {
                      def append[EE >: E, AA >: A](that: Validation[EE, AA])
                        (implicit es: Semigroup[EE], as: Semigroup[AA])
                        : Validation[EE, AA] = (this, that) match {

                        // both Success: combine results using Semigroup
                        case (Success(a1), Success(a2))    =>
                          Success(as.append(a1, a2))

                        // one side succeeds : return success value
                        case (v1@Success(a1), Failure(_)) => v1
                        case (Failure(_), v2@Success(a2)) => v2

                        // both Failures: combine errors using Semigroup
                        case (Failure(e1), Failure(e2))    =>
                          Failure(es.append(e1, e2))
                    }




Monday 18 June 12
Type Classes - effective
                     for the domain model
                    • A small data structure like Semigroup has a
                      big effect in our functional domain model
                    • How does something with just an
                      associative binary operation be so
                      effective ?
                    • The secret sauce is in the power of
                      function composition and the ability to
                      implement type classes on any abstraction


Monday 18 June 12
// using Validation as an applicative
               // can be combined to accumulate exceptions
               def makeTrade(account: Account, instrument: Instrument,
                 refNo: String, market: Market, unitPrice: BigDecimal,
                 quantity: BigDecimal) =
                 (validUnitPrice(unitPrice).liftFailNel |@|
                    validQuantity(quantity).liftFailNel) { (u, q) =>
                      Trade(account, instrument, refNo, market, u, q)
                    }




                                            an applicative builder

Monday 18 June 12
// validate quantity
               def validQuantity(qty: BigDecimal): Validation[String,
               BigDecimal] =
                 if (qty <= 0) "qty must be > 0".fail
                 else if (qty > 500) "qty must be <= 500".fail
                 else qty.success



               // validate unit price
               def validUnitPrice(price: BigDecimal):
               Validation[String, BigDecimal] =
                 if (price <= 0) "price must be > 0".fail
                 else if (price > 100) "price must be <= 100".fail
                 else price.success




Monday 18 June 12
So far in the type class
                            world ..
                    •   Type classes lead to design of abstractions that
                        can be extended post hoc
                    •   Type classes and higher order functions is a
                        potent combo of ad hoc polymorphism and
                        function composition
                    •   Don’t ignore small abstractions like Semigroup
                        and Monoid - when placed in the proper
                        context, they can contribute to writing succinct
                        code for your domain model


Monday 18 June 12
Composability FTW
                    • Functions compose mathematically. Same
                      for functional programming (a big
                      assumption - no side-effects)
                    • Side-effects don’t compose. All functional
                      programming languages encourage isolating
                      side-effects from pure domain logic .. and
                      some do enforce through the type system


Monday 18 June 12
For your domain model ..

                                              Decouple pure logic
                                              from side-effects ..
domain rules isolated
  from impure stuff


                    easier to
                    unit test
                                                   flexible reordering of
                                                    operations - easier
                                                        parallelism
                                easier to reason
                                about your logic
Monday 18 June 12
What is composition ?

                    • compose - v. to make or create by putting
                      together parts or elements
                    • a bottom up way of creating bigger
                      abstractions from smaller ones
                    • combinators FTW

Monday 18 June 12
Combinators
                    “a function which builds program fragments from program
                    fragments; in a sense the programmer using combinators
                    constructs much of the desired program automatically, rather
                    than writing every detail by hand”
                                                                  - John Hughes
                                               Generalising Monads to Arrows
                       (http://www.cse.chalmers.se/~rjmh/Papers/arrows.pdf)




Monday 18 June 12
Combinators act as the
                       glue ..
                    •   map
                    •   filter
                    •   bind
                    •   apply
                    •   ...


Monday 18 June 12
Pure functions as
                    domain behaviors
   // value a tax/fee for a specific trade
   val valueAs:
     Trade => TaxFeeId => BigDecimal = {trade => tid =>
     ((rates get tid) map (_ * principal(trade)))
        getOrElse (BigDecimal(0))
   }

   // all tax/fees for a specific trade
   val taxFeeCalculate:
     Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t =>
   tids =>
     tids zip (tids map valueAs(t))
   }




Monday 18 June 12
Pure functions as
                      domain behaviors
   // value a tax/fee for a specific trade
   val valueAs:
     Trade => TaxFeeId => BigDecimal = {trade => tid =>
                        map
     ((rates get tid) map (_ * principal(trade)))
        getOrElse (BigDecimal(0))
   }

   // all tax/fees for a specific trade
   val taxFeeCalculate:
     Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t =>
   tids =>
                zip    map
     tids zip (tids map valueAs(t))
   }
                                                 combinators
Monday 18 June 12
Pure functions as
                      domain behaviors
   // value a tax/fee for a specific trade
   val valueAs:
     Trade => TaxFeeId => BigDecimal = {trade => tid =>
                        map
     ((rates get tid) map (_ * principal(trade)))
        getOrElse (BigDecimal(0))
   }

   // all tax/fees for a specific trade
   val taxFeeCalculate:
     Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t =>
   tids =>
                zip    map
     tids zip (tids map valueAs(t))
   }
                                                 combinators
Monday 18 June 12
// enrich a trade with tax/fees and compute net value
             val enrichTrade: Trade => Trade = {trade =>
               val taxes = for {

                      // get the tax/fee ids for a trade
                      taxFeeIds      <- forTrade

                      // calculate tax fee values
                      taxFeeValues   <- taxFeeCalculate
                    }
                    yield(taxFeeIds map taxFeeValues)

                    val t = taxFeeLens.set(trade, taxes(trade))
                    netAmountLens.set(t,
                      t.taxFees.map(_.foldl(principal(t))
                                           ((a, b) => a + b._2)))
             }



Monday 18 June 12
monadic chaining for
                                       an expressive DSL

               val trds =
                 for {
                   trade         <-   trades
                   trValidated   <-   validate(trade)
                   trEnriched    <-   enrich(trValidated)
                   trFinal       <-   journalize(trEnriched)
                 }
                 yield trFinal




Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
The palette of
                    computation models

   the applicative model
    to construct trades
with accumulating validation
          errors
                      the monadic model           the arrow model
                   that chains computation         of computation
                    and gives us a nice DSL   that generalizes monads
                       for trade lifecycle
Monday 18 June 12
The Arrow model of
                       computation
                                                   a monad lifts a value
               A            => M[A]                into a computation


                                                   an arrow lifts a function
                                                    from input to output
               (B => C) => A[B,C]                    into a computation


                If the function is of the form A => M[B]
                       then we call the arrow a Kleisli
Monday 18 June 12
Back to our domain model
                - from Orders to Trades

                    1. process client orders
                    2. execute in the market
                    3. allocate street side trades to client
                       accounts




Monday 18 June 12
def tradeGeneration(market: Market, broker: Account,
          clientAccounts: List[Account]) =
          // client orders
          kleisli(clientOrders)
                // executed at market by broker
            >=> kleisli(execute(market)(broker))
                  // and allocated to client accounts
              >=> kleisli(allocate(clientAccounts))




Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
Managing States

                    • But domain objects don’t exist in isolation
                    • Need to interact with other objects
                    • .. and respond to events from the external
                      world
                    • .. changing from one state to another

Monday 18 June 12
A day in the life of a
                          Trade object
                                                 tax/fee added
                    created   value date added                                        ssi added
                                                                 net value computed




     a Trade object
                                                                            ready for settlement



Monday 18 June 12
What’s the big deal ?
      All these sound like changing states of a newly created
                          Trade object !!




Monday 18 June 12
• but ..
                    • Changing state through in-place mutation is
                      destructive

                    • We lose temporality of the data structure
                    • The fact that a Trade is enriched with tax/
                      fee NOW does not mean it was not valued
                      at 0 tax SOME TIME BACK


Monday 18 June 12
What if we would like to have our
                    system rolled back to THAT POINT IN
                                    TIME ?




Monday 18 June 12
We are just being lossy

Monday 18 June 12
• The solution is to keep information in such a
                    way that we have EVERY BIT OF DETAILS
                    stored as the object changes from one state
                    to another

               • Enter Event Sourcing
Monday 18 June 12
Event Sourcing
                    • Preserves the temporality of the data
                      structure
                    • Represent state NOT as a mutable object,
                      rather as a sequence of domain events that
                      took place right from the creation till the
                      current point in time
                    • Decouple the state of the object from its
                      identity. The state changes over time,
                      identity is IMMUTABLE

Monday 18 June 12
Domain Events as
                          Behaviors
           NewTrade             AddValueDate               EnrichTrade



            state = Created     state = ValueDateAdded         state = Enriched



  persisted with timestamp t0                                  persisted with timestamp t2

                                 persisted with timestamp t1



                                   (t2 > t1 > t0)
Monday 18 June 12
.. and since we store every event that hits the system we
      have the ability to recreate ANY previous state of the
       system starting from ANY point in time in the past




Monday 18 June 12
Events and States
                    sealed trait TradingEvent extends Event

                    case   object   NewTrade extends TradingEvent
                    case   object   EnrichTrade extends TradingEvent
                    case   object   AddValueDate extends TradingEvent
                    case   object   SendOutContractNote extends TradingEvent

                    sealed trait TradeState extends State

                    case object Created extends TradeState
                    case object Enriched extends TradeState
                    case object ValueDateAdded extends TradeState




Monday 18 June 12
The Current State
                    • How do you reflect the current state of the
                      domain object ?
                     ✦   start with the initial state
                     ✦   manage all state transitions
                     ✦   persist all state transitions
                     ✦   maintain a snapshot that reflects the
                         current state
                     ✦   you now have the ability to roll back to
                         any earlier state
Monday 18 June 12
The essence of Event
                          Sourcing
                    Store the events in a durable repository.They are
                    the lowest level granular structure that model
                    the actual behavior of the domain.You can
                    always recreate any state if you store events
                    since the creation of the domain object.




Monday 18 June 12
The Duality
                    •   Event Sourcing keeps a     •   In functional
                        trail of all events that       programming, data
                        the abstraction has            structures that keep
                        handled                        track of their history are
                                                       called persistent data
                    •   Event Sourcing does not        structures. Immutability
                                                       taken to the next level
                        ever mutate an existing
                        record
                                                   •   An immutable data
                                                       structure does not
                                                       mutate data - returns a
                                                       newer version every
                                                       time you update it


Monday 18 June 12
Event Sourced Domain
                           Models
                    • Now we have the domain objects receiving
                      domain events which take them from state
                      A to state B
                    • Will the Trade object have all the event
                      handling logic ?
                    • What about state changes ? Use the Trade
                      object for this as well ?


Monday 18 June 12
Separation of concerns

                    • The Trade object has the core business of
                      the trading logic. It manifests all state
                      changes in terms of what it contains as data
                    • But event handling and managing state
                      transitions is something that belongs to the
                      service layer of the domain model



Monday 18 June 12
Separation of Concerns
                    •   The service layer          •   The core domain layer

                        ✦   receives events from       ✦   implements core
                            the context                    trading functions like
                                                           calculation of value
                        ✦   manages state                  date, trade valuation,
                            transitions                    tax/fee handling etc

                        ✦   delegates to Trade         ✦   completely oblivious
                            object for core                of the context
                            business

                        ✦   notifies other
                            subscribers
Monday 18 June 12
Agenda
                    Immutability and algebraic data types
                    Updating domain models, functionally
                    Type classes & domain modeling
                    Models of computation
                     Managing states - the functional way
                    A declarative design for domain service layer


Monday 18 June 12
The Domain Service
                           Layer
                    • Handles domain events and delegates to
                      someone who logs (sources) the events
                    • May need to maintain an in-memory
                      snapshot of the domain object’s current
                      state
                    • Delegates persistence of the snapshot to
                      other subscribers like Query Services


Monday 18 June 12
CQRS
                    • The service layer ensures a complete
                      decoupling of how it handles updates
                      (commands) on domain objects and reads
                      (queries) on the recent snapshot
                    • Updates are persisted as events while
                      queries are served from entirely different
                      sources, typically from read slaves in an
                      RDBMS


Monday 18 June 12
In other words, the domain
                    service layer acts as a state
                              machine
Monday 18 June 12
Making it Explicit
                    • Model the domain service layer as an FSM
                      (Finite State Machine) - call it the
                      TradeLifecycle, which is totally
                      segregated from the Trade domain object

                    • .. and let the FSM run within an actor
                      model based on asynchronous messaging
                    • We use Akka’s FSM implementation
Monday 18 June 12
FSM in Akka
                    • Actor based
                     ✦   available as a mixin for the Akka actor
                     ✦   similar to Erlang gen_fsm
                         implementation
                     ✦   as a client you need to implement the
                         state transition rules using a declarative
                         syntax based DSL, all heavy lifting done by
                         Akka actors
Monday 18 June 12
initial state

                                               match on event
                                               AddValueDate

         startWith(Created, trade)
                                                                start state of Trade
                                                                       object
         when(Created) {
           case Event(e@AddValueDate, data) =>
             log.map(_.appendAsync(data.refNo, Created, Some(data), e))
             val trd = addValueDate(data)
             gossip(trd)
             goto(ValueDateAdded) using trd forMax(timeout)
         }

      notify observers                                                        log the event
                         move to next state
                                               update data
                          of Trade lifecycle
                                                structure




Monday 18 June 12
Handle events &
                                              process data updates and state
                                                         changes



         startWith(Created, trade)                            Log events (event sourcing)

         when(Created) {
           case Event(e@AddValueDate, data) =>
             log.map(_.appendAsync(data.refNo, Created, Some(data), e))
             val trd = addValueDate(data)
             gossip(trd)
             goto(ValueDateAdded) using trd forMax(timeout)
         }


                              Notifying Listeners




Monday 18 June 12
State change -
                            functionally
                // closure for adding a value date
                val addValueDate: Trade => Trade = {trade =>
                  valueDateLens.set(trade, ..)
                }




         pure referentially transparent implementation


Monday 18 June 12
Notifying Listeners

       • A typical use case is to send
       updates to the Query
       subscribers as in CQRS


       • The query is rendered from a
       separate store which needs to
       be updated with all changes that
       go on in the domain model




Monday 18 June 12
Notifications in Akka
                           FSM
             trait FSM[S, D] extends Listeners {
               //..
             }

                           Listeners is a generic trait to implement
                                listening capability on an Actor

                                    trait Listeners {self: Actor =>
                                      protected val listeners = ..
                                      //..
                                      protected def gossip(msg: Any) =
                                        listeners foreach (_ ! msg)
                                      //..
                                    }


Monday 18 June 12
class TradeLifecycle(trade: Trade, timeout: Duration,
         log: Option[EventLog])
         extends Actor with FSM[TradeState, Trade] {
         import FSM._

           startWith(Created, trade)
                                                                                  domain service as
                                                                                a Finite State Machine
           when(Created) {
             case Event(e@AddValueDate, data) =>
               log.map(_.appendAsync(data.refNo, Created, Some(data), e))   •         declarative
               val trd = addValueDate(data)                                 •        actor based
               gossip(trd)                                                  •       asynchronous
               goto(ValueDateAdded) using trd forMax(timeout)               •       event sourced
           }

           when(ValueDateAdded) {
             case Event(StateTimeout, _) =>
               stay

              case Event(e@EnrichTrade, data) =>
                log.map(_.appendAsync(data.refNo, ValueDateAdded, None,   e))
                val trd = enrichTrade(data)
                gossip(trd)
                goto(Enriched) using trd forMax(timeout)
           }
           //..
       }

Monday 18 June 12
// 1. create the state machine
             val tlc =
               system.actorOf(Props(
                 new TradeLifecycle(trd, timeout.duration, Some(log))))

             // 2. register listeners
             tlc ! SubscribeTransitionCallBack(qry)

             // 3. fire events
             tlc ! AddValueDate
             tlc ! EnrichTrade
             val future = tlc ? SendOutContractNote

             // 4. wait for result
             finalTrades += Await.result(future, timeout.duration)
                            .asInstanceOf[Trade]




Monday 18 June 12
// check query store
               val f = qry ? QueryAllTrades
               val qtrades = Await.result(f,timeout.duration)
                                  .asInstanceOf[List[Trade]]

               // query store in sync with the in-memory snapshot
               qtrades should equal(finalTrades)




Monday 18 June 12
Summary
                    •   Functional programming is programming with functions -
                        design your domain model with function composition
                        as the primary form of building abstractions
                    •   Have a clear delineation between side-effects and pure
                        functions. Pure functions are easier to debug and
                        reason about
                    •   Immutability rocks - makes it way easier to share
                        abstractions across threads
                    •   With immutable domain abstractions you can make
                        good use of transactional references through an STM


Monday 18 June 12
Thank You!



Monday 18 June 12

Weitere ähnliche Inhalte

Was ist angesagt?

ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022Alexander Ioffe
 
Functional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioFunctional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioLuis Atencio
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressionsLogan Chien
 
Functional programming with Java 8
Functional programming with Java 8Functional programming with Java 8
Functional programming with Java 8Talha Ocakçı
 
Tagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsPhilip Schwarz
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react formYao Nien Chung
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...Philip Schwarz
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
 
Power of functions in a typed world
Power of functions in a typed worldPower of functions in a typed world
Power of functions in a typed worldDebasish Ghosh
 
Java 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsJava 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsNewCircle Training
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongMario Fusco
 
Peeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfPeeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfJaroslavRegec1
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingMario Fusco
 
Fusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsPhilip Schwarz
 
Example of JAVA Program
Example of JAVA ProgramExample of JAVA Program
Example of JAVA ProgramTrenton Asbury
 
Why TypeScript?
Why TypeScript?Why TypeScript?
Why TypeScript?FITC
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Philip Schwarz
 

Was ist angesagt? (20)

ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 
Functional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis AtencioFunctional Programming in JavaScript by Luis Atencio
Functional Programming in JavaScript by Luis Atencio
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressions
 
Functional programming with Java 8
Functional programming with Java 8Functional programming with Java 8
Functional programming with Java 8
 
Tagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also Programs
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react form
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)Functional Programming Patterns (NDC London 2014)
Functional Programming Patterns (NDC London 2014)
 
Power of functions in a typed world
Power of functions in a typed worldPower of functions in a typed world
Power of functions in a typed world
 
Java 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsJava 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & Streams
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
 
Peeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfPeeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdf
 
Java 8 features
Java 8 featuresJava 8 features
Java 8 features
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
OOP and FP
OOP and FPOOP and FP
OOP and FP
 
Fusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
 
Example of JAVA Program
Example of JAVA ProgramExample of JAVA Program
Example of JAVA Program
 
Why TypeScript?
Why TypeScript?Why TypeScript?
Why TypeScript?
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
 

Mehr von Debasish Ghosh

Approximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming ApplicationsApproximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming ApplicationsDebasish Ghosh
 
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
 
Mining Functional Patterns
Mining Functional PatternsMining Functional Patterns
Mining Functional PatternsDebasish Ghosh
 
An Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain ModelingAn Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain ModelingDebasish Ghosh
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingDebasish Ghosh
 
From functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingFrom functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingDebasish Ghosh
 
Domain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approachDomain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approachDebasish Ghosh
 
Property based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesProperty based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesDebasish Ghosh
 
Big Data - architectural concerns for the new age
Big Data - architectural concerns for the new ageBig Data - architectural concerns for the new age
Big Data - architectural concerns for the new ageDebasish Ghosh
 
Functional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingFunctional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingDebasish Ghosh
 
DSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic modelDSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic modelDebasish Ghosh
 
Dependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake PatternDependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake PatternDebasish Ghosh
 

Mehr von Debasish Ghosh (12)

Approximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming ApplicationsApproximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming Applications
 
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
 
Mining Functional Patterns
Mining Functional PatternsMining Functional Patterns
Mining Functional Patterns
 
An Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain ModelingAn Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain Modeling
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain Modeling
 
From functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingFrom functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modeling
 
Domain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approachDomain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approach
 
Property based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesProperty based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rules
 
Big Data - architectural concerns for the new age
Big Data - architectural concerns for the new ageBig Data - architectural concerns for the new age
Big Data - architectural concerns for the new age
 
Functional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingFunctional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modeling
 
DSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic modelDSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic model
 
Dependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake PatternDependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake Pattern
 

Kürzlich hochgeladen

Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...DianaGray10
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7DianaGray10
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaborationbruanjhuli
 
AI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarAI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarPrecisely
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureEric D. Schabell
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdfPedro Manuel
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding TeamAdam Moalla
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024D Cloud Solutions
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationIES VE
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopBachir Benyammi
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfAijun Zhang
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URLRuncy Oommen
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostMatt Ray
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsSafe Software
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024SkyPlanner
 

Kürzlich hochgeladen (20)

Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
 
AI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity WebinarAI You Can Trust - Ensuring Success with Data Integrity Webinar
AI You Can Trust - Ensuring Success with Data Integrity Webinar
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability Adventure
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdf
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 Workshop
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdf
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URL
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
 
20150722 - AGV
20150722 - AGV20150722 - AGV
20150722 - AGV
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024
 

Domain Modeling in a Functional World

  • 1. Domain Modeling in a Functional World some real life experiences Monday 18 June 12
  • 2. Debasish Ghosh @debasishg on Twitter code @ http://github.com/debasishg blog @ Ruminations of a Programmer http://debasishg.blogspot.com Monday 18 June 12
  • 3. What is Domain Modeling • We limit ourselves strictly to how the domain behaves internally and how it responds to events that it receives from the external context • We think of a domain model as being comprised of the core granular abstractions that handle the business logic and a set of coarser level services that interacts with the external world Monday 18 June 12
  • 4. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 5. Functional domain models Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Event Sourcing A declarative design for domain service layer Monday 18 June 12
  • 6. Immutability • Immutable data ✦ can be shared freely ✦ no shared mutable state and hence no locks, semaphores etc. ✦ and you get some parallelism free Monday 18 June 12
  • 7. Immutability • Algebraic Data Types for representation of domain entities • Immutable structures like type-lenses for functional updates • No in-place mutation • Heavy use of persistent data structures Monday 18 June 12
  • 8. Algebraic Data Types Ok .. I get the Data Type part, but give me the ALGEBRA ... Monday 18 June 12
  • 9. Algebraic Data Types • For simplicity let’s assume that an algebraic data type induces an algebra which gives us some structures and some functions (or morphisms or arrows) to manipulate. So we have some types and some functions that morph one type into another • Well .. almost .. it’s actually an initial algebra, but let’s stop at that without sounding more scary Monday 18 June 12
  • 10. Rather we look at some examples .. • Unit represented by 1 • Optional data type represented as Option in Scala (Maybe in Haskell) - a Sum type 1 + X • Tuples represented as pairs (a, b) - the simplest Product type • Recursive data types - Lists of X represented by L=1+X*L • Binary Trees, represented as B = 1 + X * B2 Monday 18 June 12
  • 11. A Taste of Algebra unit type • A List[Int] can be either an empty list or consisting of one integer, or two integers, or three etc. sum type product type • So a list of integers can be represented algebraically as 1 + int + int * int + int * int * int + ... OR 1 + int + int ** 2 + int ** 3 + .. Monday 18 June 12
  • 12. and that’s not all .. • we can have Taylor series expansion, Composition, and even Differentiation on types .. Monday 18 June 12
  • 13. and that’s not all .. een llow Ha r a fo em ion th series expansion, • we can have e cuss Taylor rv and even Differentiation on se d Composition, is re.. can types we Monday 18 June 12
  • 14. Product Type Formal Definition from Bob Harper PFPL Chapter 14 : “The binary product of two types consists of ordered pairs of values, one from each type in the order specified. The associated eliminatory forms are projections, which select the first and second component of a pair. The nullary product, or unit type consists solely of the unique null tuple of no values, and has no associated eliminatory form” Monday 18 June 12
  • 15. Product Type • Implemented through case classes in Scala • In a domain model we represent entities using product types • A tuple is the simplest product type : val point = (x_cord, y_cord) Monday 18 June 12
  • 16. Representation as ADTs (product type) case class Trade(account: Account, instrument: Instrument, refNo: String, market: Market, unitPrice: BigDecimal, quantity: BigDecimal, tradeDate: Date = Calendar.getInstance.getTime, valueDate: Option[Date] = None, taxFees: Option[List[(TaxFeeId, BigDecimal)]] = None, netAmount: Option[BigDecimal] = None) { override def equals(that: Any) = refNo == that.asInstanceOf[Trade].refNo override def hashCode = refNo.hashCode } Monday 18 June 12
  • 17. Sum Type Formally from Bob Harper in PFPL, Chapter 15 : “Most data structures involve alternatives such as the distinction between a leaf and an interior node in a tree, or a choice in the outermost form of a piece of abstract syntax. Importantly, the choice determines the structure of the value. For example, nodes have children, but leaves do not, and so forth. These concepts are expressed by sum types, specifically the binary sum, which offers a choice of two things, and the nullary sum, which offers a choice of no things” Monday 18 June 12
  • 18. Sum Type • Implemented through subtyping in Scala • Option is one of the most commonly used sum type sealed abstract class Option[+A] extends Product with Serializable //.. final case class Some[+A](x: A) extends Option[A] //.. case object None extends Option[Nothing] //.. Monday 18 June 12
  • 19. Representation as ADTs (sum type) // various tax/fees to be paid when u do a trade sealed trait TaxFeeId extends Serializable case object TradeTax extends TaxFeeId case object Commission extends TaxFeeId case object VAT extends TaxFeeId Monday 18 June 12
  • 20. ADTs & Domain Modeling • Encouraging immutability • In Scala you can use vars to have mutable case classes, but that’s not encouraged • With Haskell algebraic data types are immutable values and you can use things like the State monad for implicit state update • There are some specialized data structures that allow functional updates e.g. Lens, Zipper etc. Monday 18 June 12
  • 21. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 22. Updating a Domain Structure functionally • A Type-Lens is a data structure that sets up a bidirectional transformation between a set of source structures S and target structures T • A Type-Lens is set up as a pair of functions: • get S -> T • putBack (S X T) -> S Monday 18 June 12
  • 23. Type Lens in Scala case class Lens[A, B] ( get: A => B, set: (A, B) => A ) //.. Monday 18 June 12
  • 24. A Type Lens in Scala // change ref no val refNoLens: Lens[Trade, String] = Lens((t: Trade) => t.refNo, (t: Trade, r: String) => t.copy(refNo = r)) a function that takes a trade and returns it’s reference no a function that updates a trade with a supplied reference no Monday 18 June 12
  • 25. Lens under Composition • What’s the big deal with a Type Lens ? ✦ Lens compose and hence gives you a cool syntax to update nested structures within an ADT def addressL: Lens[Person, Address] = ... def streetL: Lens[Address, String] = ... val personStreetL: Lens[Person, String] = streetL compose addressL Monday 18 June 12
  • 26. Lens under composition Using the personStreetL lens we may access or set the (indirect) street property of a Person instance val str: String = personStreetL get person val newP: Person = personStreetL set (person, "Bob_St") Monday 18 June 12
  • 27. Functional updates in our domain model using Type Lens // change ref no val refNoLens: Lens[Trade, String] = Lens((t: Trade) => t.refNo, (t: Trade, r: String) => t.copy(refNo = r)) // add tax/fees val taxFeeLens: Lens[Trade, Option[List[(TaxFeeId, BigDecimal)]]] = Lens((t: Trade) => t.taxFees, (t: Trade, tfs: Option[List[(TaxFeeId, BigDecimal)]]) => t.copy(taxFees = tfs)) // add net amount val netAmountLens: Lens[Trade, Option[BigDecimal]] = Lens((t: Trade) => t.netAmount, (t: Trade, n: Option[BigDecimal]) => t.copy(netAmount = n)) // add value date val valueDateLens: Lens[Trade, Option[Date]] = Lens((t: Trade) => t.valueDate, (t: Trade, d: Option[Date]) => t.copy(valueDate = d)) Monday 18 June 12
  • 28. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 29. Type class • Ad hoc polymorphism • Open, unlike subtyping - makes more sense in domain modeling because domain rules also change. Useful for designing open APIs which can be adapted later to newer types • Leads to generic, modular and reusable code Monday 18 June 12
  • 30. Useful type classes behind your domain model • Functor - offers you the capability to map over a structure. And not surprisingly, it has a single method: map trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] //.. } Monday 18 June 12
  • 31. More type classes • Applicative Functors - Beefed up functors. Here the function is wrapped within a functor. So we lift a function wrapped in a functor to apply on another functor trait Applicative[F[_]] extends Functor { def apply[A, B](f: F[A => B]): F[A] => F[B] def pure[A](a: A): F[A] //.. } Monday 18 June 12
  • 32. More type classes • Monads - beefed up Applicative Functor, where in addition to apply and map, you get a bind (>>=) function which helps you bind a monadic value to a function’s input that produces a monadic output trait Monad[F[_]] extends Applicative { def >>=[A, B] (fa: F[A])(f: A => F[B]): F[B] } Monday 18 June 12
  • 33. And some more .. • Semigroup - a type class that offers an associative binary operation trait Semigroup[F] { def append(f1: F, f2: => F): F //.. } • Monoid - a Semigroup with an identity element trait Monoid[F] extends Semigroup[F] { def zero: F //.. } Monday 18 June 12
  • 34. Accumulating validation errors using Type Classes • We can use Semigroup to accumulate errors in our domain validation logic • Semigroup is an abstraction that offers an associative binary append operation - looks intuitive for our use case Monday 18 June 12
  • 35. Accumulating validation errors using Type Classes sealed trait Validation[+e, +A] { def append[EE >: E, AA >: A](that: Validation[EE, AA]) (implicit es: Semigroup[EE], as: Semigroup[AA]) : Validation[EE, AA] = (this, that) match { // both Success: combine results using Semigroup case (Success(a1), Success(a2)) => Success(as.append(a1, a2)) // one side succeeds : return success value case (v1@Success(a1), Failure(_)) => v1 case (Failure(_), v2@Success(a2)) => v2 // both Failures: combine errors using Semigroup case (Failure(e1), Failure(e2)) => Failure(es.append(e1, e2)) } Monday 18 June 12
  • 36. Type Classes - effective for the domain model • A small data structure like Semigroup has a big effect in our functional domain model • How does something with just an associative binary operation be so effective ? • The secret sauce is in the power of function composition and the ability to implement type classes on any abstraction Monday 18 June 12
  • 37. // using Validation as an applicative // can be combined to accumulate exceptions def makeTrade(account: Account, instrument: Instrument, refNo: String, market: Market, unitPrice: BigDecimal, quantity: BigDecimal) = (validUnitPrice(unitPrice).liftFailNel |@| validQuantity(quantity).liftFailNel) { (u, q) => Trade(account, instrument, refNo, market, u, q) } an applicative builder Monday 18 June 12
  • 38. // validate quantity def validQuantity(qty: BigDecimal): Validation[String, BigDecimal] = if (qty <= 0) "qty must be > 0".fail else if (qty > 500) "qty must be <= 500".fail else qty.success // validate unit price def validUnitPrice(price: BigDecimal): Validation[String, BigDecimal] = if (price <= 0) "price must be > 0".fail else if (price > 100) "price must be <= 100".fail else price.success Monday 18 June 12
  • 39. So far in the type class world .. • Type classes lead to design of abstractions that can be extended post hoc • Type classes and higher order functions is a potent combo of ad hoc polymorphism and function composition • Don’t ignore small abstractions like Semigroup and Monoid - when placed in the proper context, they can contribute to writing succinct code for your domain model Monday 18 June 12
  • 40. Composability FTW • Functions compose mathematically. Same for functional programming (a big assumption - no side-effects) • Side-effects don’t compose. All functional programming languages encourage isolating side-effects from pure domain logic .. and some do enforce through the type system Monday 18 June 12
  • 41. For your domain model .. Decouple pure logic from side-effects .. domain rules isolated from impure stuff easier to unit test flexible reordering of operations - easier parallelism easier to reason about your logic Monday 18 June 12
  • 42. What is composition ? • compose - v. to make or create by putting together parts or elements • a bottom up way of creating bigger abstractions from smaller ones • combinators FTW Monday 18 June 12
  • 43. Combinators “a function which builds program fragments from program fragments; in a sense the programmer using combinators constructs much of the desired program automatically, rather than writing every detail by hand” - John Hughes Generalising Monads to Arrows (http://www.cse.chalmers.se/~rjmh/Papers/arrows.pdf) Monday 18 June 12
  • 44. Combinators act as the glue .. • map • filter • bind • apply • ... Monday 18 June 12
  • 45. Pure functions as domain behaviors // value a tax/fee for a specific trade val valueAs: Trade => TaxFeeId => BigDecimal = {trade => tid => ((rates get tid) map (_ * principal(trade))) getOrElse (BigDecimal(0)) } // all tax/fees for a specific trade val taxFeeCalculate: Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t => tids => tids zip (tids map valueAs(t)) } Monday 18 June 12
  • 46. Pure functions as domain behaviors // value a tax/fee for a specific trade val valueAs: Trade => TaxFeeId => BigDecimal = {trade => tid => map ((rates get tid) map (_ * principal(trade))) getOrElse (BigDecimal(0)) } // all tax/fees for a specific trade val taxFeeCalculate: Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t => tids => zip map tids zip (tids map valueAs(t)) } combinators Monday 18 June 12
  • 47. Pure functions as domain behaviors // value a tax/fee for a specific trade val valueAs: Trade => TaxFeeId => BigDecimal = {trade => tid => map ((rates get tid) map (_ * principal(trade))) getOrElse (BigDecimal(0)) } // all tax/fees for a specific trade val taxFeeCalculate: Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t => tids => zip map tids zip (tids map valueAs(t)) } combinators Monday 18 June 12
  • 48. // enrich a trade with tax/fees and compute net value val enrichTrade: Trade => Trade = {trade => val taxes = for { // get the tax/fee ids for a trade taxFeeIds <- forTrade // calculate tax fee values taxFeeValues <- taxFeeCalculate } yield(taxFeeIds map taxFeeValues) val t = taxFeeLens.set(trade, taxes(trade)) netAmountLens.set(t, t.taxFees.map(_.foldl(principal(t)) ((a, b) => a + b._2))) } Monday 18 June 12
  • 49. monadic chaining for an expressive DSL val trds = for { trade <- trades trValidated <- validate(trade) trEnriched <- enrich(trValidated) trFinal <- journalize(trEnriched) } yield trFinal Monday 18 June 12
  • 50. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 51. The palette of computation models the applicative model to construct trades with accumulating validation errors the monadic model the arrow model that chains computation of computation and gives us a nice DSL that generalizes monads for trade lifecycle Monday 18 June 12
  • 52. The Arrow model of computation a monad lifts a value A => M[A] into a computation an arrow lifts a function from input to output (B => C) => A[B,C] into a computation If the function is of the form A => M[B] then we call the arrow a Kleisli Monday 18 June 12
  • 53. Back to our domain model - from Orders to Trades 1. process client orders 2. execute in the market 3. allocate street side trades to client accounts Monday 18 June 12
  • 54. def tradeGeneration(market: Market, broker: Account, clientAccounts: List[Account]) = // client orders kleisli(clientOrders) // executed at market by broker >=> kleisli(execute(market)(broker)) // and allocated to client accounts >=> kleisli(allocate(clientAccounts)) Monday 18 June 12
  • 55. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 56. Managing States • But domain objects don’t exist in isolation • Need to interact with other objects • .. and respond to events from the external world • .. changing from one state to another Monday 18 June 12
  • 57. A day in the life of a Trade object tax/fee added created value date added ssi added net value computed a Trade object ready for settlement Monday 18 June 12
  • 58. What’s the big deal ? All these sound like changing states of a newly created Trade object !! Monday 18 June 12
  • 59. • but .. • Changing state through in-place mutation is destructive • We lose temporality of the data structure • The fact that a Trade is enriched with tax/ fee NOW does not mean it was not valued at 0 tax SOME TIME BACK Monday 18 June 12
  • 60. What if we would like to have our system rolled back to THAT POINT IN TIME ? Monday 18 June 12
  • 61. We are just being lossy Monday 18 June 12
  • 62. • The solution is to keep information in such a way that we have EVERY BIT OF DETAILS stored as the object changes from one state to another • Enter Event Sourcing Monday 18 June 12
  • 63. Event Sourcing • Preserves the temporality of the data structure • Represent state NOT as a mutable object, rather as a sequence of domain events that took place right from the creation till the current point in time • Decouple the state of the object from its identity. The state changes over time, identity is IMMUTABLE Monday 18 June 12
  • 64. Domain Events as Behaviors NewTrade AddValueDate EnrichTrade state = Created state = ValueDateAdded state = Enriched persisted with timestamp t0 persisted with timestamp t2 persisted with timestamp t1 (t2 > t1 > t0) Monday 18 June 12
  • 65. .. and since we store every event that hits the system we have the ability to recreate ANY previous state of the system starting from ANY point in time in the past Monday 18 June 12
  • 66. Events and States sealed trait TradingEvent extends Event case object NewTrade extends TradingEvent case object EnrichTrade extends TradingEvent case object AddValueDate extends TradingEvent case object SendOutContractNote extends TradingEvent sealed trait TradeState extends State case object Created extends TradeState case object Enriched extends TradeState case object ValueDateAdded extends TradeState Monday 18 June 12
  • 67. The Current State • How do you reflect the current state of the domain object ? ✦ start with the initial state ✦ manage all state transitions ✦ persist all state transitions ✦ maintain a snapshot that reflects the current state ✦ you now have the ability to roll back to any earlier state Monday 18 June 12
  • 68. The essence of Event Sourcing Store the events in a durable repository.They are the lowest level granular structure that model the actual behavior of the domain.You can always recreate any state if you store events since the creation of the domain object. Monday 18 June 12
  • 69. The Duality • Event Sourcing keeps a • In functional trail of all events that programming, data the abstraction has structures that keep handled track of their history are called persistent data • Event Sourcing does not structures. Immutability taken to the next level ever mutate an existing record • An immutable data structure does not mutate data - returns a newer version every time you update it Monday 18 June 12
  • 70. Event Sourced Domain Models • Now we have the domain objects receiving domain events which take them from state A to state B • Will the Trade object have all the event handling logic ? • What about state changes ? Use the Trade object for this as well ? Monday 18 June 12
  • 71. Separation of concerns • The Trade object has the core business of the trading logic. It manifests all state changes in terms of what it contains as data • But event handling and managing state transitions is something that belongs to the service layer of the domain model Monday 18 June 12
  • 72. Separation of Concerns • The service layer • The core domain layer ✦ receives events from ✦ implements core the context trading functions like calculation of value ✦ manages state date, trade valuation, transitions tax/fee handling etc ✦ delegates to Trade ✦ completely oblivious object for core of the context business ✦ notifies other subscribers Monday 18 June 12
  • 73. Agenda Immutability and algebraic data types Updating domain models, functionally Type classes & domain modeling Models of computation Managing states - the functional way A declarative design for domain service layer Monday 18 June 12
  • 74. The Domain Service Layer • Handles domain events and delegates to someone who logs (sources) the events • May need to maintain an in-memory snapshot of the domain object’s current state • Delegates persistence of the snapshot to other subscribers like Query Services Monday 18 June 12
  • 75. CQRS • The service layer ensures a complete decoupling of how it handles updates (commands) on domain objects and reads (queries) on the recent snapshot • Updates are persisted as events while queries are served from entirely different sources, typically from read slaves in an RDBMS Monday 18 June 12
  • 76. In other words, the domain service layer acts as a state machine Monday 18 June 12
  • 77. Making it Explicit • Model the domain service layer as an FSM (Finite State Machine) - call it the TradeLifecycle, which is totally segregated from the Trade domain object • .. and let the FSM run within an actor model based on asynchronous messaging • We use Akka’s FSM implementation Monday 18 June 12
  • 78. FSM in Akka • Actor based ✦ available as a mixin for the Akka actor ✦ similar to Erlang gen_fsm implementation ✦ as a client you need to implement the state transition rules using a declarative syntax based DSL, all heavy lifting done by Akka actors Monday 18 June 12
  • 79. initial state match on event AddValueDate startWith(Created, trade) start state of Trade object when(Created) { case Event(e@AddValueDate, data) => log.map(_.appendAsync(data.refNo, Created, Some(data), e)) val trd = addValueDate(data) gossip(trd) goto(ValueDateAdded) using trd forMax(timeout) } notify observers log the event move to next state update data of Trade lifecycle structure Monday 18 June 12
  • 80. Handle events & process data updates and state changes startWith(Created, trade) Log events (event sourcing) when(Created) { case Event(e@AddValueDate, data) => log.map(_.appendAsync(data.refNo, Created, Some(data), e)) val trd = addValueDate(data) gossip(trd) goto(ValueDateAdded) using trd forMax(timeout) } Notifying Listeners Monday 18 June 12
  • 81. State change - functionally // closure for adding a value date val addValueDate: Trade => Trade = {trade => valueDateLens.set(trade, ..) } pure referentially transparent implementation Monday 18 June 12
  • 82. Notifying Listeners • A typical use case is to send updates to the Query subscribers as in CQRS • The query is rendered from a separate store which needs to be updated with all changes that go on in the domain model Monday 18 June 12
  • 83. Notifications in Akka FSM trait FSM[S, D] extends Listeners { //.. } Listeners is a generic trait to implement listening capability on an Actor trait Listeners {self: Actor => protected val listeners = .. //.. protected def gossip(msg: Any) = listeners foreach (_ ! msg) //.. } Monday 18 June 12
  • 84. class TradeLifecycle(trade: Trade, timeout: Duration, log: Option[EventLog]) extends Actor with FSM[TradeState, Trade] { import FSM._ startWith(Created, trade) domain service as a Finite State Machine when(Created) { case Event(e@AddValueDate, data) => log.map(_.appendAsync(data.refNo, Created, Some(data), e)) • declarative val trd = addValueDate(data) • actor based gossip(trd) • asynchronous goto(ValueDateAdded) using trd forMax(timeout) • event sourced } when(ValueDateAdded) { case Event(StateTimeout, _) => stay case Event(e@EnrichTrade, data) => log.map(_.appendAsync(data.refNo, ValueDateAdded, None, e)) val trd = enrichTrade(data) gossip(trd) goto(Enriched) using trd forMax(timeout) } //.. } Monday 18 June 12
  • 85. // 1. create the state machine val tlc = system.actorOf(Props( new TradeLifecycle(trd, timeout.duration, Some(log)))) // 2. register listeners tlc ! SubscribeTransitionCallBack(qry) // 3. fire events tlc ! AddValueDate tlc ! EnrichTrade val future = tlc ? SendOutContractNote // 4. wait for result finalTrades += Await.result(future, timeout.duration) .asInstanceOf[Trade] Monday 18 June 12
  • 86. // check query store val f = qry ? QueryAllTrades val qtrades = Await.result(f,timeout.duration) .asInstanceOf[List[Trade]] // query store in sync with the in-memory snapshot qtrades should equal(finalTrades) Monday 18 June 12
  • 87. Summary • Functional programming is programming with functions - design your domain model with function composition as the primary form of building abstractions • Have a clear delineation between side-effects and pure functions. Pure functions are easier to debug and reason about • Immutability rocks - makes it way easier to share abstractions across threads • With immutable domain abstractions you can make good use of transactional references through an STM Monday 18 June 12