A note of the Functional and Algebraic Domain Modeling talk given by Debasish Ghosh.

## FP, Algebra and Domain Modeling

Fis programming with pure function. output purely determined by the input. Pure mapping between values. No assignment, no side-effects Functions compose. Expression-oriented programming.

John Carmack: sometimes, the elegant implementation is just a function. Not a method, not a class , not a framework. Functions are mathematics, and maths are elegant, hardly anything can be more elegant than math.

Algebra: is the study of algebraic structures. An algebraic structure has three parts:

• A set (carrier set or underlying set) with
• one or more finitely operations defined on the set
• satisfies a list of axioms

Algebraic thinking is reasoning about code in terms of data types and the operations they support without considering the underlying operation implementations.

For function f : A => B and g: B => C, wen can compose them as h: A => C only when they are our functions that don’t throw exception or perform any side effects.

Domain is described in a set of bounded contexts. A bounded context has a consistent vocabulary, a set of domain behaviors modeled as functions on domain objects implemented as types, each of the behaviors honor a set of business rules, related behaviors grouped as modules. Modules have algebraic structures.

Functions work on types (domain objects).

Domain model = union of bounded context (i). A bounded context has a set of modules. A module has a set of types and a set of functions and business rules.

Domain functions can be composed and are closed under composition.

Functions are morphisms, types are sets. There are composition and rules/laws.

Domain model algebra: algebra of types, functions and laws of the solution domain model. Explicit: types, type constraints, functions between types. Verifiable: Type Constraints, more with DT, algebraic property based testing.

## Algebra of Types and Modules

Sum types: boolean, enumeration, option, either, failure(try).
Using sealed trait and case class/objects. Then pattern match with exhaustive checking.

More algebra of types: exponential f: A => B has b ** a inhabitants, Taylor series: recursive data types Derivatives: zippers

A function is a mapping from the domain of types to the co-domain of types, that’s an algebra of a function.

A module is a collection of related functions, that’s an algebra of a module.

A domain model is a collection of modules, that is a domain model algebra.

Generic: parametric data types. Lis is a type constructor . Clear separation between the contract (the algebra) and its implementation (interpreters). Standard vocabulary (like pattern). Existing set of reusable algebras offered by libraries.

Example:

1. identify domain behaviors
2. identify the algebra of functions (not implementation)
3. compose algebras to form large behaviors (a program)
4. Plug in concrete types to complete the implementation.

## An Example

Issues: composition and side effects (violates modularity). Use algebra, type constructor, as a rescue to fix side effects.

 ``````1 2 3 4 5 6 7 `````` ``````trait Trading { def fromClientOrder: ClientOrder => Order def execute(market, Market, brokerAccount: Account): Order => List[Execution] def allocate(accounts: List[Account]): List[Execution] => List[Trade] } trait TradeComponent extends Trading with Logging with Auditing``````

The effect type `F[_]` make the module an effectful module and the functions effectful functions. In `F[A]`, `F` is the side effect, `A` is the type of result. `F[_]` is an opaque type that handles the error.

 `````` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 `````` ``````trait Trading[F[_]] { def fromClientOrder: ClientOrder => F[Order] def execute(market, Market, brokerAccount: Account): Order => F[List[Execution]] def allocate(accounts: List[Account]): List[Execution] => F[List[Trade]] } // trading is the interpreter/implementation that generates the effect F[_] // we want to run them sequentially: behavior composition def tradeGenerationProgram[M[_]: Monad](trading: Trading[M]) = for { order <- trading.fromClientOrder(clientOrder) executions <- trading.execute(market, brokerAccount, order) trades <- trading.allocate(List(account1, account2, account3), executions) } yield trades // with Logging def tradeGenerationWithLogPrgram[M[_]: Monad]( trading: Trading[M], logger: Logger[M]) = for { _ <- logger.info("start processing") order <- trading.fromClientOrder(clientOrder) executions <- trading.execute(market, brokerAccount, order) trades <- trading.allocate(List(account1, account2, account3), executions) _ <- logger.info("allocatioin done") } yield trades``````

An interpreter is an implementaiton of the domain model. It handles the error path of execution and the actual effect calls. A sample interpreter could be:

 `````` 1 2 3 4 5 6 7 8 9 10 11 12 13 `````` ``````class TradingInterpreter[F[_]] (implict me: MonadError[F, Throwable]) extends Trading[F] { def fromClientOrder: ClientOrder => F[Order] = makeOrder(_) match { case Left(error) => me.rasieError(new Exception(error.mesage)) case Right(order) => order.pure[F] } def execute(market: Market, brokerAccount: Account): Order => F[List[Execution]] = ??? def allocate(accounts: List[Account]): List[Execution] => F[List[Trade]] = ??? } class LoggerIntepreter[F[_]] extends Logger[F] { def info(message: String) = ??? }``````

A big benefit of module is composition.

Finally, the runtime. `[IO]` or `[Task]` deal with the effects such as input/output and execution.

 `````` 1 2 3 4 5 6 7 8 9 10 11 12 `````` ``````import cats.effect.IO object TradingComponent extends TradingInterpreter[IO] tradeGenerationProgram(TradingComponent).unsafeRunSync // run with logging object LoggerComponent extends LoggerIntepreter[IO] tradeGenerationWithLogPrgram(TradingComponent, LoggerComponent).unsafeRunSync // or use a different runtime import monix.eval.Task object TradingComponent extends TradingInterpreter[Task] tradeGenerationProgram(TradingComponent)``````

Effects and side-effects are not the same thing. Effects are algebraic and are good, side-effects are bugs.

## Takeaways

• Algebra scales from a single data type to an entire bounded context.
• Algebras compose enabling composition of domain behaviors.
• Algebras let you focus on the compositionality without any context of implementation.
• Statically typed functional programming is programming with algebras.
• Abstract early, interprete as late as possible.
• Abstractions/functions compose only when they are abstract and parametric.
• Modularity in the presence of side-effects is a challenge.
• Effects as algebras are pure values that can compose based on laws.
• Honor the law of using the least powerful abstraction that works.