SlideShare a Scribd company logo
1 of 39
Download to read offline
Debasish Ghosh, Sep 22, 2022
Functional Domain Modeling
The ZIO 2 way
What is a domain model ?
A domain model in problem solving and software engineering is a
conceptual model of all the topics related to a speci
fi
c problem. It
describes the various entities, their attributes, roles, and relationships,
plus the constraints that govern the problem domain. It does not
describe the solutions to the problem.
Wikipedia (http://en.wikipedia.org/wiki/Domain_model)
https://msdn.microsoft.com/en-us/library/jj591560.aspx
A Pattern Language
for domain modeling
A Pattern Language
for domain modeling
•Common vocabulary
A Pattern Language
for domain modeling
•Common vocabulary
•Modularization
• Nouns in the model
• Pure objects
• Names consistent with domain vocabulary
• Strongly typed (newtypes, refinement types)
• Provides object storage and access
• Abstractions with Effects
• Names consistent with the aggregate root that gets stored
• Presents clients with simple interfaces to manage persistence
• Decouples model design from persistence technology
• Provides object storage and access
• Abstractions with Effects
• Names consistent with the aggregate root that gets stored
• Presents clients with simple interfaces to manage persistence
• Decouples model design from persistence technology
Client code ignores repository implementation, developers do not
- Eric Evans in the DDD blue book
• Coarse grained abstractions
• Relates to domain concept not specific to a single entity
• Interface defined in terms of other elements of the model
• Abstractions with Effects
A Pattern Language
for domain modeling
final case class Account private (
no: AccountNo,
name: AccountName,
dateOfOpen: ZonedDateTime,
//..
)
Model definition of an entity as an algebraic data type
• Types with names derived from domain
vocabulary
• Rich semantics with newtypes,
re
fi
nement types etc. from zio-prelude
final case class Account private (
no: AccountNo,
name: AccountName,
dateOfOpen: ZonedDateTime,
//..
)
object Account {
def tradingAccount(
no: String,
name: String,
openDate: Option[ZonedDateTime],
//..
): Validation[String, Account] = { //..
private[model] def validateAccountNo(
no: String): Validation[String, AccountNo] = //..
//..
def close(
a: Account,
closeDate: ZonedDateTime
): Validation[String, Account] = { //..
}
Model definition of an entity as an algebraic data type A Module for the model containing definitions for
smart constructors, validations, and other domain
logic for the model
• Types with names derived from domain
vocabulary
• Rich semantics with newtypes,
re
fi
nement types etc. from zio-prelude
• Abstractions that compose
• Build larger abstractions - validations
for entire domain entity as a
composition of smaller validations
• Supported by zio-prelude
• Smart constructor
• Domain behaviour
final case class Account private (
no: AccountNo,
name: AccountName,
dateOfOpen: ZonedDateTime,
//..
)
object Account {
def tradingAccount(
no: String,
name: String,
openDate: Option[ZonedDateTime],
//..
): Validation[String, Account] = { //..
private[model] def validateAccountNo(
no: String): Validation[String, AccountNo] = //..
//..
def close(
a: Account,
closeDate: ZonedDateTime
): Validation[String, Account] = { //..
}
Model definition of an entity as an algebraic data type A Module for the model containing definitions for
smart constructors, validations, and other domain
logic for the model
• Types with names derived from domain
vocabulary
• Rich semantics with newtypes,
re
fi
nement types etc. from zio-prelude
• Abstractions that compose
• Build larger abstractions - validations
for entire domain entity as a
composition of smaller validations
• Supported by zio-prelude
• Smart constructor
• Domain behaviour
•Pure model
•Functional core
•Compositional with zio-prelude
Repository
trait AccountRepository {
/** query by account number */
def queryByAccountNo(no: AccountNo): Task[Option[Account]]
/** store */
def store(a: Account): Task[Account]
/** store many */
def store(as: List[Account]): Task[Unit]
/** query by opened date */
def allOpenedOn(openedOnDate: LocalDate): Task[List[Account]]
//..
}
Repository
trait AccountRepository {
/** query by account number */
def queryByAccountNo(no: AccountNo): Task[Option[Account]]
/** store */
def store(a: Account): Task[Account]
/** store many */
def store(as: List[Account]): Task[Unit]
/** query by opened date */
def allOpenedOn(openedOnDate: LocalDate): Task[List[Account]]
//..
}
• Effectful contract
• Concrete ZIO effect (unlike tagless
fi
nal)
• Less parametric than the tagless
fi
nal approach
• No dependency on any speci
fi
c environment
• zio.Task[A] => ZIO[Any,Throwable,A]
Repository - Implementations
final case class AccountRepositoryLive(
xaResource: Resource[Task, Transactor[Task]])
extends AccountRepository {
import AccountRepositoryLive.SQL
def all: Task[List[Account]] =
xaResource.use { xa =>
SQL.getAll
.to[List]
.transact(xa)
.orDie
}
def queryByAccountNo(no: AccountNo): Task[Option[Account]] =
xaResource.use { xa =>
SQL
.get(no.value.value)
.option
.transact(xa)
.orDie
}
//..
}
• Implementation
details
• Functional core
Repository - Implementations
final case class AccountRepositoryLive(
xaResource: Resource[Task, Transactor[Task]])
extends AccountRepository {
import AccountRepositoryLive.SQL
def all: Task[List[Account]] =
xaResource.use { xa =>
SQL.getAll
.to[List]
.transact(xa)
.orDie
}
def queryByAccountNo(no: AccountNo): Task[Option[Account]] =
xaResource.use { xa =>
SQL
.get(no.value.value)
.option
.transact(xa)
.orDie
}
//..
}
object AccountRepositoryLive extends CatzInterop {
val layer: ZLayer[DBConfig, Throwable, AccountRepository] = {
ZLayer
.scoped(for {
cfg <- ZIO.service[DBConfig]
transactor <- mkTransactor(cfg)
} yield new AccountRepositoryLive(transactor))
}
//..
}
• Implementation
details
• Functional core
• ZLayer as a recipe for creating ZIO services from dependencies
• ZLayer serves as the factory for creating domain artifacts
• The scoped API ensures that resource lifetimes are properly
managed
• Inject the proper layer as dependency
REPOSITORIES have many advantages, including the following:
• They present clients with a simple model for obtaining persistent
objects and managing their life cycle.
• They decouple application and domain design from persistence
technology, multiple database strategies, or even multiple data
sources
• They communicate design decisions about object access
• They allow easy substitution of dummy implementation, for use in
testing (typically using an in-memory allocation)
- Eric Evans (The Blue DDD book)
REPOSITORIES have many advantages, including the following:
• They present clients with a simple model for obtaining persistent
objects and managing their life cycle.
• They decouple application and domain design from persistence
technology, multiple database strategies, or even multiple data
sources
• They communicate design decisions about object access
• They allow easy substitution of dummy implementation, for use in
testing (typically using an in-memory allocation)
- Eric Evans (The Blue DDD book)
Single interface principle with return types
that abstract the value as well as handling of
failures through zio effects
REPOSITORIES have many advantages, including the following:
• They present clients with a simple model for obtaining persistent
objects and managing their life cycle.
• They decouple application and domain design from persistence
technology, multiple database strategies, or even multiple data
sources
• They communicate design decisions about object access
• They allow easy substitution of dummy implementation, for use in
testing (typically using an in-memory allocation)
- Eric Evans (The Blue DDD book)
Single interface principle with return types
that abstract the value as well as handling of
failures through zio effects
Allow multiple implementations (the OO way)
that can be injected dynamically and
compositionally through ZLayers
REPOSITORIES have many advantages, including the following:
• They present clients with a simple model for obtaining persistent
objects and managing their life cycle.
• They decouple application and domain design from persistence
technology, multiple database strategies, or even multiple data
sources
• They communicate design decisions about object access
• They allow easy substitution of dummy implementation, for use in
testing (typically using an in-memory allocation)
- Eric Evans (The Blue DDD book)
Single interface principle with return types
that abstract the value as well as handling of
failures through zio effects
Allow multiple implementations (the OO way)
that can be injected dynamically and
compositionally through ZLayers
The concrete zio effect that gets returned
clearly indicates semantics of failure handling,
environment dependencies (if any) and the
exception types
REPOSITORIES have many advantages, including the following:
• They present clients with a simple model for obtaining persistent
objects and managing their life cycle.
• They decouple application and domain design from persistence
technology, multiple database strategies, or even multiple data
sources
• They communicate design decisions about object access
• They allow easy substitution of dummy implementation, for use in
testing (typically using an in-memory allocation)
- Eric Evans (The Blue DDD book)
Single interface principle with return types
that abstract the value as well as handling of
failures through zio effects
Allow multiple implementations (the OO way)
that can be injected dynamically and
compositionally through ZLayers
The concrete zio effect that gets returned
clearly indicates semantics of failure handling,
environment dependencies (if any) and the
exception types
ZLayers! ZLayers!
Services
trait TradingService {
def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]]
def getTrades(forAccountNo: AccountNo,
forDate: Option[LocalDate] = None
): IO[TradingError, List[Trade]]
def getTradesByISINCodes(forDate: LocalDate,
forIsins: Set[model.instrument.ISINCode]
): IO[TradingError, List[Trade]]
def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder]
): IO[TradingError, NonEmptyList[Order]]
def execute(orders: NonEmptyList[Order],
market: Market,
brokerAccountNo: AccountNo
): IO[TradingError, NonEmptyList[Execution]]
def allocate(executions: NonEmptyList[Execution],
clientAccounts: NonEmptyList[AccountNo],
userId: UserId
): IO[TradingError, NonEmptyList[Trade]]
}
• Effectful contracts
• Concrete ZIO effect (unlike tagless
fi
nal) - clearly publishes
all exceptions and returned value types
• Less parametric than the tagless
fi
nal approach
• No dependency on any speci
fi
c environment
• zio.IO[A] => ZIO[Any, E, A]
• Coarse grained abstractions - not speci
fi
c to any entity
• All names derived from domain vocabulary
Services
trait TradingService {
def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]]
def getTrades(forAccountNo: AccountNo,
forDate: Option[LocalDate] = None
): IO[TradingError, List[Trade]]
def getTradesByISINCodes(forDate: LocalDate,
forIsins: Set[model.instrument.ISINCode]
): IO[TradingError, List[Trade]]
def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder]
): IO[TradingError, NonEmptyList[Order]]
def execute(orders: NonEmptyList[Order],
market: Market,
brokerAccountNo: AccountNo
): IO[TradingError, NonEmptyList[Execution]]
def allocate(executions: NonEmptyList[Execution],
clientAccounts: NonEmptyList[AccountNo],
userId: UserId
): IO[TradingError, NonEmptyList[Trade]]
}
Services
trait TradingService {
def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]]
def getTrades(forAccountNo: AccountNo,
forDate: Option[LocalDate] = None
): IO[TradingError, List[Trade]]
def getTradesByISINCodes(forDate: LocalDate,
forIsins: Set[model.instrument.ISINCode]
): IO[TradingError, List[Trade]]
def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder]
): IO[TradingError, NonEmptyList[Order]]
def execute(orders: NonEmptyList[Order],
market: Market,
brokerAccountNo: AccountNo
): IO[TradingError, NonEmptyList[Execution]]
def allocate(executions: NonEmptyList[Execution],
clientAccounts: NonEmptyList[AccountNo],
userId: UserId
): IO[TradingError, NonEmptyList[Trade]]
}
• Effectful contracts
• Concrete ZIO effect (unlike tagless
fi
nal) - clearly publishes
all exceptions and returned value types
• Less parametric than the tagless
fi
nal approach
• No dependency on any speci
fi
c environment
• zio.IO[A] => ZIO[Any, E, A]
• Coarse grained abstractions - not speci
fi
c to any entity
• All names derived from domain vocabulary
Services - Functional Core
final def generateTrade(
frontOfficeInput: GenerateTradeFrontOfficeInput,
userId: UserId
): IO[Throwable, NonEmptyList[Trade]] = {
for {
orders <- orders(frontOfficeInput.frontOfficeOrders)
executions <- execute(
orders,
frontOfficeInput.market,
frontOfficeInput.brokerAccountNo
)
trades <- allocate(executions, frontOfficeInput.clientAccountNos, userId)
} yield trades
}
Services - Implementation
final case class TradingServiceLive(
ar: AccountRepository,
or: OrderRepository,
er: ExecutionRepository,
tr: TradeRepository
) extends TradingService { //..
object TradingServiceLive {
val layer: ZLayer[
AccountRepository with OrderRepository with
ExecutionRepository with TradeRepository,
Nothing,
TradingServiceLive
] = ZLayer.fromFunction(TradingServiceLive.apply _)
}
• Dependencies passed as constructor arguments
• And automatically becomes part of the ZLayer and hence
part of the dependency graph
• Dependencies are interfaces only and not implementations
Services
Wiring up ..
ZIO
.serviceWithZIO[TradingService](service => //..)
.provide(
AccountRepositoryLive.layer,
TradingServiceLive.layer,
OrderRepositoryLive.layer,
TradeRepositoryLive.layer,
ExecutionRepositoryLive.layer,
config.live
)
Wiring up ..
ZIO
.serviceWithZIO[TradingService](service => //..)
.provide(
AccountRepositoryLive.layer,
TradingServiceLive.layer,
OrderRepositoryLive.layer,
TradeRepositoryLive.layer,
ExecutionRepositoryLive.layer,
config.live
)
• Declarative way to build an instance of your domain
service from the speci
fi
ed dependencies
• ZLayers
- Allow automatic construction of the dependency
graph of your domain model
- Are composable
- Are asynchronous and non-blocking
- Can be acquired in parallel unlike class constructors
Entities
Value Objects
<<pure>>
• pure
• functional
• statically typed
• use zio-prelude for
newtypes,
re
fi
nement types,
validations etc.
Entities
Value Objects
<<pure>> Repositories
<<interfaces>>
Implementations
• pure
• functional
• statically typed
• use zio-prelude for
newtypes,
re
fi
nement types,
validations etc.
• plain old scala traits
• one per aggregate root
• effectual contracts
• concrete zio effects
• persistence platform dependent
• pass dependencies as constructor arguments
• can be multiple for a single contract
Entities
Value Objects
<<pure>> Repositories
<<interfaces>>
Implementations
Domain
Services
Domain
Service
Implementations
<<interfaces>>
• pure
• functional
• statically typed
• use zio-prelude for
newtypes,
re
fi
nement types,
validations etc.
• plain old scala traits
• one per aggregate root
• effectual contracts
• concrete zio effects
• persistence platform dependent
• pass dependencies as constructor arguments
• can be multiple for a single contract
• coarse grained
• can interact with multiple domain entities
• effectual contracts
• concrete zio effects
• pass dependencies as constructor
arguments
• depends only on repository interfaces and
NOT implementations
Entities
Value Objects
<<pure>> Repositories
<<interfaces>>
Implementations
Domain
Services
Domain
Service
Implementations
<<interfaces>>
Application
• pure
• functional
• statically typed
• use zio-prelude for
newtypes,
re
fi
nement types,
validations etc.
• plain old scala traits
• one per aggregate root
• effectual contracts
• concrete zio effects
• persistence platform dependent
• pass dependencies as constructor arguments
• can be multiple for a single contract
• coarse grained
• can interact with multiple domain entities
• effectual contracts
• concrete zio effects
• pass dependencies as constructor
arguments
• depends only on repository interfaces and
NOT implementations
• ZLayer magic!
Entities
Value Objects
<<pure>> Repositories
<<interfaces>>
Implementations
Domain
Services
Domain
Service
Implementations
<<interfaces>>
Application
• pure
• functional
• statically typed
• use zio-prelude for
newtypes,
re
fi
nement types,
validations etc.
• plain old scala traits
• one per aggregate root
• effectual contracts
• concrete zio effects
• persistence platform dependent
• pass dependencies as constructor arguments
• can be multiple for a single contract
• coarse grained
• can interact with multiple domain entities
• effectual contracts
• concrete zio effects
• pass dependencies as constructor
arguments
• depends only on repository interfaces and
NOT implementations
• ZLayer magic!
Questions ?

More Related Content

What's hot

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
Mario Fusco
 

What's hot (20)

Applicative Functor
Applicative FunctorApplicative Functor
Applicative Functor
 
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
 
Java 8 Lambda and Streams
Java 8 Lambda and StreamsJava 8 Lambda and Streams
Java 8 Lambda and Streams
 
Vert.x for Microservices Architecture
Vert.x for Microservices ArchitectureVert.x for Microservices Architecture
Vert.x for Microservices Architecture
 
Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.Java 8 Stream API. A different way to process collections.
Java 8 Stream API. A different way to process collections.
 
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
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Asynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFutureAsynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFuture
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with Cats
 
LINQ
LINQLINQ
LINQ
 
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...
 
Java exception-handling
Java exception-handlingJava exception-handling
Java exception-handling
 
Lock-free algorithms for Kotlin Coroutines
Lock-free algorithms for Kotlin CoroutinesLock-free algorithms for Kotlin Coroutines
Lock-free algorithms for Kotlin Coroutines
 
Error Management: Future vs ZIO
Error Management: Future vs ZIOError Management: Future vs ZIO
Error Management: Future vs ZIO
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Lambda Expressions in Java
Lambda Expressions in JavaLambda Expressions in Java
Lambda Expressions in Java
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Javascript functions
Javascript functionsJavascript functions
Javascript functions
 
Applicative style programming
Applicative style programmingApplicative style programming
Applicative style programming
 

Similar to Functional Domain Modeling - The ZIO 2 Way

Similar to Functional Domain Modeling - The ZIO 2 Way (20)

ZZ BC#7.5 asp.net mvc practice and guideline refresh!
ZZ BC#7.5 asp.net mvc practice  and guideline refresh! ZZ BC#7.5 asp.net mvc practice  and guideline refresh!
ZZ BC#7.5 asp.net mvc practice and guideline refresh!
 
UNIT IV DESIGN PATTERNS.pptx
UNIT IV DESIGN PATTERNS.pptxUNIT IV DESIGN PATTERNS.pptx
UNIT IV DESIGN PATTERNS.pptx
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
 
Introduction to design_patterns
Introduction to design_patternsIntroduction to design_patterns
Introduction to design_patterns
 
Software design with Domain-driven design
Software design with Domain-driven design Software design with Domain-driven design
Software design with Domain-driven design
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
 
Design pattern
Design patternDesign pattern
Design pattern
 
Most Useful Design Patterns
Most Useful Design PatternsMost Useful Design Patterns
Most Useful Design Patterns
 
Design Patterns
Design PatternsDesign Patterns
Design Patterns
 
Better Understanding OOP using C#
Better Understanding OOP using C#Better Understanding OOP using C#
Better Understanding OOP using C#
 
Agile Secure Cloud Application Development Management
Agile Secure Cloud Application Development ManagementAgile Secure Cloud Application Development Management
Agile Secure Cloud Application Development Management
 
How to design an application correctly ?
How to design an application correctly ?How to design an application correctly ?
How to design an application correctly ?
 
Sterling for Windows Phone 7
Sterling for Windows Phone 7Sterling for Windows Phone 7
Sterling for Windows Phone 7
 
Design Pattern lecture 2
Design Pattern lecture 2Design Pattern lecture 2
Design Pattern lecture 2
 
ZendCon 2011 UnCon Domain-Driven Design
ZendCon 2011 UnCon Domain-Driven DesignZendCon 2011 UnCon Domain-Driven Design
ZendCon 2011 UnCon Domain-Driven Design
 
JavaOne 2014 - Supporting Multi-tenancy Applications with Java EE
JavaOne 2014 - Supporting Multi-tenancy Applications with Java EEJavaOne 2014 - Supporting Multi-tenancy Applications with Java EE
JavaOne 2014 - Supporting Multi-tenancy Applications with Java EE
 
Kelis king - introduction to software design
Kelis king -  introduction to software designKelis king -  introduction to software design
Kelis king - introduction to software design
 
An Introduction to Domain Driven Design in PHP
An Introduction to Domain Driven Design in PHPAn Introduction to Domain Driven Design in PHP
An Introduction to Domain Driven Design in PHP
 
Application Architecture April 2014
Application Architecture April 2014Application Architecture April 2014
Application Architecture April 2014
 
Unit 2
Unit 2Unit 2
Unit 2
 

More from Debasish Ghosh

More from Debasish Ghosh (13)

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
 
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
 

Recently uploaded

Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 

Recently uploaded (20)

Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
SQL Injection Introduction and Prevention
SQL Injection Introduction and PreventionSQL Injection Introduction and Prevention
SQL Injection Introduction and Prevention
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
What need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java DevelopersWhat need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java Developers
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
Crafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM IntegrationCrafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM Integration
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
AI Hackathon.pptx
AI                        Hackathon.pptxAI                        Hackathon.pptx
AI Hackathon.pptx
 
How to pick right visual testing tool.pdf
How to pick right visual testing tool.pdfHow to pick right visual testing tool.pdf
How to pick right visual testing tool.pdf
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 

Functional Domain Modeling - The ZIO 2 Way

  • 1. Debasish Ghosh, Sep 22, 2022 Functional Domain Modeling The ZIO 2 way
  • 2. What is a domain model ? A domain model in problem solving and software engineering is a conceptual model of all the topics related to a speci fi c problem. It describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain. It does not describe the solutions to the problem. Wikipedia (http://en.wikipedia.org/wiki/Domain_model)
  • 4.
  • 5. A Pattern Language for domain modeling
  • 6. A Pattern Language for domain modeling •Common vocabulary
  • 7. A Pattern Language for domain modeling •Common vocabulary •Modularization
  • 8. • Nouns in the model • Pure objects • Names consistent with domain vocabulary • Strongly typed (newtypes, refinement types)
  • 9. • Provides object storage and access • Abstractions with Effects • Names consistent with the aggregate root that gets stored • Presents clients with simple interfaces to manage persistence • Decouples model design from persistence technology
  • 10. • Provides object storage and access • Abstractions with Effects • Names consistent with the aggregate root that gets stored • Presents clients with simple interfaces to manage persistence • Decouples model design from persistence technology Client code ignores repository implementation, developers do not - Eric Evans in the DDD blue book
  • 11. • Coarse grained abstractions • Relates to domain concept not specific to a single entity • Interface defined in terms of other elements of the model • Abstractions with Effects
  • 12. A Pattern Language for domain modeling
  • 13. final case class Account private ( no: AccountNo, name: AccountName, dateOfOpen: ZonedDateTime, //.. ) Model definition of an entity as an algebraic data type • Types with names derived from domain vocabulary • Rich semantics with newtypes, re fi nement types etc. from zio-prelude
  • 14. final case class Account private ( no: AccountNo, name: AccountName, dateOfOpen: ZonedDateTime, //.. ) object Account { def tradingAccount( no: String, name: String, openDate: Option[ZonedDateTime], //.. ): Validation[String, Account] = { //.. private[model] def validateAccountNo( no: String): Validation[String, AccountNo] = //.. //.. def close( a: Account, closeDate: ZonedDateTime ): Validation[String, Account] = { //.. } Model definition of an entity as an algebraic data type A Module for the model containing definitions for smart constructors, validations, and other domain logic for the model • Types with names derived from domain vocabulary • Rich semantics with newtypes, re fi nement types etc. from zio-prelude • Abstractions that compose • Build larger abstractions - validations for entire domain entity as a composition of smaller validations • Supported by zio-prelude • Smart constructor • Domain behaviour
  • 15. final case class Account private ( no: AccountNo, name: AccountName, dateOfOpen: ZonedDateTime, //.. ) object Account { def tradingAccount( no: String, name: String, openDate: Option[ZonedDateTime], //.. ): Validation[String, Account] = { //.. private[model] def validateAccountNo( no: String): Validation[String, AccountNo] = //.. //.. def close( a: Account, closeDate: ZonedDateTime ): Validation[String, Account] = { //.. } Model definition of an entity as an algebraic data type A Module for the model containing definitions for smart constructors, validations, and other domain logic for the model • Types with names derived from domain vocabulary • Rich semantics with newtypes, re fi nement types etc. from zio-prelude • Abstractions that compose • Build larger abstractions - validations for entire domain entity as a composition of smaller validations • Supported by zio-prelude • Smart constructor • Domain behaviour •Pure model •Functional core •Compositional with zio-prelude
  • 16. Repository trait AccountRepository { /** query by account number */ def queryByAccountNo(no: AccountNo): Task[Option[Account]] /** store */ def store(a: Account): Task[Account] /** store many */ def store(as: List[Account]): Task[Unit] /** query by opened date */ def allOpenedOn(openedOnDate: LocalDate): Task[List[Account]] //.. }
  • 17. Repository trait AccountRepository { /** query by account number */ def queryByAccountNo(no: AccountNo): Task[Option[Account]] /** store */ def store(a: Account): Task[Account] /** store many */ def store(as: List[Account]): Task[Unit] /** query by opened date */ def allOpenedOn(openedOnDate: LocalDate): Task[List[Account]] //.. } • Effectful contract • Concrete ZIO effect (unlike tagless fi nal) • Less parametric than the tagless fi nal approach • No dependency on any speci fi c environment • zio.Task[A] => ZIO[Any,Throwable,A]
  • 18. Repository - Implementations final case class AccountRepositoryLive( xaResource: Resource[Task, Transactor[Task]]) extends AccountRepository { import AccountRepositoryLive.SQL def all: Task[List[Account]] = xaResource.use { xa => SQL.getAll .to[List] .transact(xa) .orDie } def queryByAccountNo(no: AccountNo): Task[Option[Account]] = xaResource.use { xa => SQL .get(no.value.value) .option .transact(xa) .orDie } //.. } • Implementation details • Functional core
  • 19. Repository - Implementations final case class AccountRepositoryLive( xaResource: Resource[Task, Transactor[Task]]) extends AccountRepository { import AccountRepositoryLive.SQL def all: Task[List[Account]] = xaResource.use { xa => SQL.getAll .to[List] .transact(xa) .orDie } def queryByAccountNo(no: AccountNo): Task[Option[Account]] = xaResource.use { xa => SQL .get(no.value.value) .option .transact(xa) .orDie } //.. } object AccountRepositoryLive extends CatzInterop { val layer: ZLayer[DBConfig, Throwable, AccountRepository] = { ZLayer .scoped(for { cfg <- ZIO.service[DBConfig] transactor <- mkTransactor(cfg) } yield new AccountRepositoryLive(transactor)) } //.. } • Implementation details • Functional core • ZLayer as a recipe for creating ZIO services from dependencies • ZLayer serves as the factory for creating domain artifacts • The scoped API ensures that resource lifetimes are properly managed • Inject the proper layer as dependency
  • 20. REPOSITORIES have many advantages, including the following: • They present clients with a simple model for obtaining persistent objects and managing their life cycle. • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources • They communicate design decisions about object access • They allow easy substitution of dummy implementation, for use in testing (typically using an in-memory allocation) - Eric Evans (The Blue DDD book)
  • 21. REPOSITORIES have many advantages, including the following: • They present clients with a simple model for obtaining persistent objects and managing their life cycle. • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources • They communicate design decisions about object access • They allow easy substitution of dummy implementation, for use in testing (typically using an in-memory allocation) - Eric Evans (The Blue DDD book) Single interface principle with return types that abstract the value as well as handling of failures through zio effects
  • 22. REPOSITORIES have many advantages, including the following: • They present clients with a simple model for obtaining persistent objects and managing their life cycle. • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources • They communicate design decisions about object access • They allow easy substitution of dummy implementation, for use in testing (typically using an in-memory allocation) - Eric Evans (The Blue DDD book) Single interface principle with return types that abstract the value as well as handling of failures through zio effects Allow multiple implementations (the OO way) that can be injected dynamically and compositionally through ZLayers
  • 23. REPOSITORIES have many advantages, including the following: • They present clients with a simple model for obtaining persistent objects and managing their life cycle. • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources • They communicate design decisions about object access • They allow easy substitution of dummy implementation, for use in testing (typically using an in-memory allocation) - Eric Evans (The Blue DDD book) Single interface principle with return types that abstract the value as well as handling of failures through zio effects Allow multiple implementations (the OO way) that can be injected dynamically and compositionally through ZLayers The concrete zio effect that gets returned clearly indicates semantics of failure handling, environment dependencies (if any) and the exception types
  • 24. REPOSITORIES have many advantages, including the following: • They present clients with a simple model for obtaining persistent objects and managing their life cycle. • They decouple application and domain design from persistence technology, multiple database strategies, or even multiple data sources • They communicate design decisions about object access • They allow easy substitution of dummy implementation, for use in testing (typically using an in-memory allocation) - Eric Evans (The Blue DDD book) Single interface principle with return types that abstract the value as well as handling of failures through zio effects Allow multiple implementations (the OO way) that can be injected dynamically and compositionally through ZLayers The concrete zio effect that gets returned clearly indicates semantics of failure handling, environment dependencies (if any) and the exception types ZLayers! ZLayers!
  • 25.
  • 26. Services trait TradingService { def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]] def getTrades(forAccountNo: AccountNo, forDate: Option[LocalDate] = None ): IO[TradingError, List[Trade]] def getTradesByISINCodes(forDate: LocalDate, forIsins: Set[model.instrument.ISINCode] ): IO[TradingError, List[Trade]] def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder] ): IO[TradingError, NonEmptyList[Order]] def execute(orders: NonEmptyList[Order], market: Market, brokerAccountNo: AccountNo ): IO[TradingError, NonEmptyList[Execution]] def allocate(executions: NonEmptyList[Execution], clientAccounts: NonEmptyList[AccountNo], userId: UserId ): IO[TradingError, NonEmptyList[Trade]] } • Effectful contracts • Concrete ZIO effect (unlike tagless fi nal) - clearly publishes all exceptions and returned value types • Less parametric than the tagless fi nal approach • No dependency on any speci fi c environment • zio.IO[A] => ZIO[Any, E, A] • Coarse grained abstractions - not speci fi c to any entity • All names derived from domain vocabulary
  • 27. Services trait TradingService { def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]] def getTrades(forAccountNo: AccountNo, forDate: Option[LocalDate] = None ): IO[TradingError, List[Trade]] def getTradesByISINCodes(forDate: LocalDate, forIsins: Set[model.instrument.ISINCode] ): IO[TradingError, List[Trade]] def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder] ): IO[TradingError, NonEmptyList[Order]] def execute(orders: NonEmptyList[Order], market: Market, brokerAccountNo: AccountNo ): IO[TradingError, NonEmptyList[Execution]] def allocate(executions: NonEmptyList[Execution], clientAccounts: NonEmptyList[AccountNo], userId: UserId ): IO[TradingError, NonEmptyList[Trade]] }
  • 28. Services trait TradingService { def getAccountsOpenedOn(openDate: LocalDate): IO[TradingError, List[Account]] def getTrades(forAccountNo: AccountNo, forDate: Option[LocalDate] = None ): IO[TradingError, List[Trade]] def getTradesByISINCodes(forDate: LocalDate, forIsins: Set[model.instrument.ISINCode] ): IO[TradingError, List[Trade]] def orders(frontOfficeOrders: NonEmptyList[FrontOfficeOrder] ): IO[TradingError, NonEmptyList[Order]] def execute(orders: NonEmptyList[Order], market: Market, brokerAccountNo: AccountNo ): IO[TradingError, NonEmptyList[Execution]] def allocate(executions: NonEmptyList[Execution], clientAccounts: NonEmptyList[AccountNo], userId: UserId ): IO[TradingError, NonEmptyList[Trade]] } • Effectful contracts • Concrete ZIO effect (unlike tagless fi nal) - clearly publishes all exceptions and returned value types • Less parametric than the tagless fi nal approach • No dependency on any speci fi c environment • zio.IO[A] => ZIO[Any, E, A] • Coarse grained abstractions - not speci fi c to any entity • All names derived from domain vocabulary
  • 29. Services - Functional Core final def generateTrade( frontOfficeInput: GenerateTradeFrontOfficeInput, userId: UserId ): IO[Throwable, NonEmptyList[Trade]] = { for { orders <- orders(frontOfficeInput.frontOfficeOrders) executions <- execute( orders, frontOfficeInput.market, frontOfficeInput.brokerAccountNo ) trades <- allocate(executions, frontOfficeInput.clientAccountNos, userId) } yield trades }
  • 30. Services - Implementation final case class TradingServiceLive( ar: AccountRepository, or: OrderRepository, er: ExecutionRepository, tr: TradeRepository ) extends TradingService { //.. object TradingServiceLive { val layer: ZLayer[ AccountRepository with OrderRepository with ExecutionRepository with TradeRepository, Nothing, TradingServiceLive ] = ZLayer.fromFunction(TradingServiceLive.apply _) } • Dependencies passed as constructor arguments • And automatically becomes part of the ZLayer and hence part of the dependency graph • Dependencies are interfaces only and not implementations
  • 32. Wiring up .. ZIO .serviceWithZIO[TradingService](service => //..) .provide( AccountRepositoryLive.layer, TradingServiceLive.layer, OrderRepositoryLive.layer, TradeRepositoryLive.layer, ExecutionRepositoryLive.layer, config.live )
  • 33. Wiring up .. ZIO .serviceWithZIO[TradingService](service => //..) .provide( AccountRepositoryLive.layer, TradingServiceLive.layer, OrderRepositoryLive.layer, TradeRepositoryLive.layer, ExecutionRepositoryLive.layer, config.live ) • Declarative way to build an instance of your domain service from the speci fi ed dependencies • ZLayers - Allow automatic construction of the dependency graph of your domain model - Are composable - Are asynchronous and non-blocking - Can be acquired in parallel unlike class constructors
  • 34. Entities Value Objects <<pure>> • pure • functional • statically typed • use zio-prelude for newtypes, re fi nement types, validations etc.
  • 35. Entities Value Objects <<pure>> Repositories <<interfaces>> Implementations • pure • functional • statically typed • use zio-prelude for newtypes, re fi nement types, validations etc. • plain old scala traits • one per aggregate root • effectual contracts • concrete zio effects • persistence platform dependent • pass dependencies as constructor arguments • can be multiple for a single contract
  • 36. Entities Value Objects <<pure>> Repositories <<interfaces>> Implementations Domain Services Domain Service Implementations <<interfaces>> • pure • functional • statically typed • use zio-prelude for newtypes, re fi nement types, validations etc. • plain old scala traits • one per aggregate root • effectual contracts • concrete zio effects • persistence platform dependent • pass dependencies as constructor arguments • can be multiple for a single contract • coarse grained • can interact with multiple domain entities • effectual contracts • concrete zio effects • pass dependencies as constructor arguments • depends only on repository interfaces and NOT implementations
  • 37. Entities Value Objects <<pure>> Repositories <<interfaces>> Implementations Domain Services Domain Service Implementations <<interfaces>> Application • pure • functional • statically typed • use zio-prelude for newtypes, re fi nement types, validations etc. • plain old scala traits • one per aggregate root • effectual contracts • concrete zio effects • persistence platform dependent • pass dependencies as constructor arguments • can be multiple for a single contract • coarse grained • can interact with multiple domain entities • effectual contracts • concrete zio effects • pass dependencies as constructor arguments • depends only on repository interfaces and NOT implementations • ZLayer magic!
  • 38. Entities Value Objects <<pure>> Repositories <<interfaces>> Implementations Domain Services Domain Service Implementations <<interfaces>> Application • pure • functional • statically typed • use zio-prelude for newtypes, re fi nement types, validations etc. • plain old scala traits • one per aggregate root • effectual contracts • concrete zio effects • persistence platform dependent • pass dependencies as constructor arguments • can be multiple for a single contract • coarse grained • can interact with multiple domain entities • effectual contracts • concrete zio effects • pass dependencies as constructor arguments • depends only on repository interfaces and NOT implementations • ZLayer magic!