Reinventing the
Transaction Script
(NDC London 2020)
@ScottWlaschin
fsharpforfunandprofit.com
Are transaction scripts
a Bad Thing?
Are transaction scripts
a Bad Thing?
No!
Part I
What is a "Transaction Script"?
What is a "Transaction Script"?
Question:
How should you organize
your domain logic?
Transaction Script
• Most business applications can be thought of
as a series of transactions.
• A "Transaction Script" organizes this logic as a
single procedure, making calls directly to the
database or through a thin database wrapper.
• Each transaction will have its ownTransaction
Script
The PEAA patterns for domain logic
• Transaction script: each procedure handles a
single request
• Table Module: one instance handles the
business logic for all rows in a database
table/view.
• Domain Model: An object model of the domain
that incorporates both behavior and data.
• Service Layer: a layer of services that
establishes a set of available operations
The PEAA patterns for domain logic
• Transaction script
• Table Module
• Domain Model
• Service Layer
Too simple, too "anemic"
Too db centric
Nice and complex!
The developer's curse
Simple Complex
Transaction scripts have a bad rep
• The reputation
– It's for people who are stuck in the 1990s
– It's for people who use Cobol
– It's only for uncool people
• The truth
– It can be reinvented using functional programming
– Therefore, it can now be cool again!
The pros and cons of
Transaction Scripts
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Pro: Easy to understand.
One directional.
Pro: Focused!
All code is relevant.
YAGNI for free
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Con: Hard to modify
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else if some other condition then
some more business logic
else
some more business logic
some more DB access
Con: Hard to modify
and evolve.
No graceful path to
richer behavior
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Con: Hard to reuse.
No rich domain model.
How do you reuse this bit only?
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Con: Hard to test
Business logic is
mixed up with DB access
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Con: Hard to test
How can you test
just these bits?
A typical transaction script
get data from DB
do some business logic
write some data to DB
some more business logic
if some condition then
some more DB access
some more business logic
some more DB access
else
some more business logic
some more DB access
Can we fix
these problems?
Yes, we can!
The fix:
Use Functional Programming
& Domain Driven Design
High coolness score
Part II
Intro to Functional Programming
Function
The Tunnel of
Transformation
Function
apple -> banana
A function is a thing which
transforms inputs to outputs
A function is a standalone thing,
not attached to a class
It can be used for inputs and outputs
of other functions
input
A function can be an output
A function is a standalone thing
output
A function can be an input
A function is a standalone thing
input output
A function can be a parameter
A function is a standalone thing
Core FP principle:
Composition everywhere
What is Composition?
Lego Philosophy
1. All pieces are designed to be connected
2. The pieces are reusable in many contexts
3. Connect two pieces together and get
another "piece" that can still be connected
All pieces are designed to be connected
The pieces are reusable in different contexts
Connect two pieces together and
get another "piece" that can still be connected
Make big things from small things in the same way
Function Composition
Function composition
Function 1
apple -> banana
Function 2
banana -> cherry
Function composition
>>
Function 1
apple -> banana
Function 2
banana -> cherry
Function composition
New Function
apple -> cherry
Can't tell it was built
from smaller functions!
Where did the banana go?
(abstraction)
Building big things from functions
It's compositions all the way up
Low-level operation
ToUpper
stringstring
Low-level operation
Service
AddressValidator
Validation
Result
Address
Low-level operation Low-level operation
Service
Use-case
UpdateProfileData
ChangeProfile
Result
ChangeProfile
Request
Service Service
Use-case
Web application
Http
Response
Http
Request
Use-case Use-case
Http
Response
Http
Request
Check out my "Power
of Composition" talk
for more details.
Composition everywhere:
Types can be composed too
Algebraic type system
New types are built from smaller types by:
Composing with “AND”
Composing with “OR”
Example: pairs, tuples, records
FruitSalad = One each of and and
Compose with “AND”
type FruitSalad = {
Apple: AppleVariety
Banana: BananaVariety
Cherry: CherryVariety
}
Snack = or or
Compose with “OR”
type Snack =
| Apple of AppleVariety
| Banana of BananaVariety
| Cherry of CherryVariety
Real world example
of type composition
Example of some requirements:
We accept three forms of payment:
Cash, PayPal, or Card.
For Cash we don't need any extra information
For PayPal we need an email address
For Cards we need a card type and card number
interface IPaymentMethod
{..}
class Cash() : IPaymentMethod
{..}
class PayPal(string emailAddress): IPaymentMethod
{..}
class Card(string cardType, string cardNo) : IPaymentMethod
{..}
In OO design you would probably implement it as an
interface and a set of subclasses, like this:
type EmailAddress = string
type CardNumber = string
In FP you would probably implement by composing
types, like this:
type EmailAddress = ...
type CardNumber = …
type CardType = Visa | Mastercard
type CreditCardInfo = {
CardType : CardType
CardNumber : CardNumber
}
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD
type Payment = {
Amount : PaymentAmount
Currency: Currency
Method: PaymentMethod }
type EmailAddress = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| PayPal of EmailAddress
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD
type Payment = {
Amount : PaymentAmount
Currency: Currency
Method: PaymentMethod }
Applying FP principles to
Transaction Scripts
Introducing "Workflows"
A.k.a. "Transaction", "Story" , "Use-case"
Check out "Event Storming" for
understanding business events
Implementing workflows
A workflow will be
implemented by a function!
Composable
Type
Composable
Type
Implementing workflows
Inputs and outputs are
defined by composable types
Implementing workflows
We will compose a workflow
from smaller components
Each component is a
standalone function
Part III
Intro to Domain-Driven Design
The DDD book came out just
after PEAA in 2003
"Focus on the domain and domain
logic rather than technology"
-- Eric Evans
Agile
Agile
Domain-Driven Design
Domain-Driven Design
• Agile contribution:
– Rapid feedback during design
• DDD contribution:
– Stakeholders have a shared mental model
– …which is also represented in the code
How can we do design right?
Key DDD principle:
Communicate the design
in the code
Can you really make code
represent the domain?
What some source code looks like
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = {Name:string; Hand:Hand}
type Game = {Deck:Deck; Players: Player list}
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
Sharedlanguage What DDD source code
*should* look like
(this is F#)
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = {Name:string; Hand:Hand}
type Game = {Deck:Deck; Players: Player list}
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
* means a pair. Choose one from each type
list type is built in
Deal
(original)
Deck
(remaining)
Deck
(on table)
Card
Modeling a workflow with a function
type Deal = Deck -> (Deck * Card)
Input Output
Pickup Card
(updated)
Hand
(original)
Hand
(on table)
Card
Modeling a workflow with a function
type PickupCard = (Hand * Card) –> Hand
Input Output
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
Can non-programmers provide
useful feedback?
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight | …
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight | …
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
In the real world
Suit
Rank
Card
Hand
Deck
Player
Deal
In the code
Suit
Rank
Card
Hand
Deck
Player
Deal
In the real world
Suit
Rank
Card
Hand
Deck
Player
Deal
In the code
Suit
Rank
Card
Hand
Deck
Player
Deal
PlayerController
DeckBase
AbstractCardProxyFactoryBean

module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight | …
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = { Name:string; Hand:Hand }
type Game = { Deck:Deck; Players: Player list }
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
Key DDD principle:
Communicate the design
in the code

Introducing "bounded contexts"
Grouping functionality in the domain
A bounded
context
Why "Bounded Context"?
• "Context"
– Specialized knowledge and common language
– Information taken out of context is confusing or
unusable
• "Bounded"
– Contexts must be autonomous so they can
evolve independently.
– Boundaries keep you focused! No scope creep!
Bounded context
Bounded context with workflows
A bounded context contains a set of
related workflows
Bounded context with workflows
A bounded context contains a set of
related workflows/functions
Part IV
Reinventing the Transaction Script
• FP contribution:
– Transactions are functions
– Build them from components using composition
• Functional Domain Modeling contribution:
– Use composable types for a rich domain model
– The domain actions are standalone/reusable
– Use autonomous bounded contexts to group and
manage the workflows
Reinventing the Transaction Script
Pros and cons of Transaction Scripts
Pros
• Easy to understand
• Focused: All code is
relevant.
Cons
• Hard to modify/evolve
• Hard to reuse
• No rich domain model
• Hard to test
Claim:
FP-style transaction scripts
are easier to comprehend than
OO domain models.
FP workflow
All arrows go left to right
Example: a web backend
One directional flow,
even with branching
Object Oriented workflow
Arrows go in all directions
We don't design microservices this way!
Real-world OO dependency graph
Multiple cyclic dependencies 
Real-world FP dependency graph
All arrows go left to right 
Same functionality
as the OO example
Claim:
FP-style transaction scripts
are resistant to bloat
Some OO-style interfaces
interface IRepository {
Insert
InsertAsync
Delete
DeleteAsync
Update
UpdateAsync
CommitChanges
CommitChangesAsync
Query
QueryByKey
QueryWithFilter
QueryWithSpecification
Contains
Count
QuerySummary
QuerySummaryV2
ChangePassword
ChangePasswordV2
DeleteAllRows
LaunchMissiles
LaunchMissilesV2
}
One interface that supports *every*
possible workflow!
Where's the
Interface segregation principle?
FP workflow
Transaction script style functions
only contain what they need.
Every part is relevant.
You get the ISP for free.
Claim:
FP-style transaction scripts
can be modified with confidence
Modifying a workflow
Replace a component
Static type checking ensures that
sub-components are used correctly
Minimizing the amount of code that I touch
Add features to a workflow
Insert new logic
Minimizing the amount of code that I touch
Add branching to a workflow
Add conditional logic
Parameterizing a workflow for reuse
Swap out parameters for reuse
as long as input and output types match
Claim:
FP-style transaction scripts
can have a rich domain model
type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
A rich domain model...
These functions are independent, so these
workflows can still evolve independently
...used in functions
Sharing rich domain components
Shared component
Yes, complex domain logic *is*
compatible with transaction scripts
Claim:
FP-style transaction scripts
are the natural evolution of
Onion/Clean/Hexagonal
Architecture
Traditional layered model
A change to the way that a workflow works
means that you need to touch every layer.
Vertical slices
Each workflow contains all the code it needs to get its job done.
When the requirements change for a workflow, only the code in
that particular vertical slice needs to change.
Vertical slices stretched out
Confusing!

The "onion" architecture
Core domain is pure, and all I/O is at the edges

See "Functional Core/Imperative Shell"
Claim:
FP-style transaction scripts
are easy to test
Review of Key Testing Concepts
• The SUT (System Under Test) should be a unit of
business value
– Test transactions, not classes
• Tests should apply to the boundaries of a system
not its internals
– Tests should be done at the transaction level
• A "Unit" test means the test is isolated
– That is, it produces no side effects and can be run in
isolation.
– A unit is not a class!
In a functional design, all
I/O is at the edges.
A FP-style transaction script
Database Database
Load data
Process it
Save it
DeterministicNon-deterministic Non-deterministic
This makes testing easy!
I/O in the middle of a workflow
Keep them separate
FP style transactions work
on the front end too!
event-driven!
MVU is a FP style approach
As seen in Elm, Redux, etc
MVU is a FP style approach
Deterministic
function
Deterministic
function
Non-deterministic
actions ("I/O")
PartV
Deployment options for
FP-style transaction scripts
3 different architectures…
• For monoliths:
– Each bounded context is a separate module with a
well-defined interface
• For service-oriented architecture:
– Each bounded context is a separate container
• For serverless:
– Each individual workflow is deployed separately
In conclusion:
The ReinventedTransaction Script
The ReinventedTransaction Script
• Most business applications can be thought of
as a series of transactions.
• A "Transaction Script" organizes this logic as a
single function, with a deterministic core
and I/O at the edges.
• Each transaction will have its own,
autonomous, evolvableTransaction Script
In conclusion…
Transaction scripts should be more popular
• Business-focused not technology-focused
• Great for agile:
– "Transaction" as the unit of development
• Easy to understand: dataflow is one directional
• Less bloat. You get ISP andYAGNI for free!
In conclusion…
Problems are solved by
• FP composability
• Separation of I/O
New, improved transaction scripts!
• Have a rich domain
• Are easy to modify
• Are easy to test
• Are microservice and serverless friendly!
"Reinventing theTransaction Script"
– Slides and video will be posted at
• fsharpforfunandprofit.com/transactionscript
Related talks
– "The Power of Composition"
• fsharpforfunandprofit.com/composition
– "Domain Modeling Made Functional"
• fsharpforfunandprofit.com/ddd
Thanks!
Twitter: @ScottWlaschin

Reinventing the Transaction Script (NDC London 2020)

  • 1.
    Reinventing the Transaction Script (NDCLondon 2020) @ScottWlaschin fsharpforfunandprofit.com
  • 2.
  • 3.
  • 4.
    Part I What isa "Transaction Script"?
  • 5.
    What is a"Transaction Script"?
  • 6.
    Question: How should youorganize your domain logic?
  • 7.
    Transaction Script • Mostbusiness applications can be thought of as a series of transactions. • A "Transaction Script" organizes this logic as a single procedure, making calls directly to the database or through a thin database wrapper. • Each transaction will have its ownTransaction Script
  • 8.
    The PEAA patternsfor domain logic • Transaction script: each procedure handles a single request • Table Module: one instance handles the business logic for all rows in a database table/view. • Domain Model: An object model of the domain that incorporates both behavior and data. • Service Layer: a layer of services that establishes a set of available operations
  • 9.
    The PEAA patternsfor domain logic • Transaction script • Table Module • Domain Model • Service Layer Too simple, too "anemic" Too db centric Nice and complex!
  • 10.
  • 11.
    Transaction scripts havea bad rep • The reputation – It's for people who are stuck in the 1990s – It's for people who use Cobol – It's only for uncool people • The truth – It can be reinvented using functional programming – Therefore, it can now be cool again!
  • 12.
    The pros andcons of Transaction Scripts
  • 13.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Pro: Easy to understand. One directional. Pro: Focused! All code is relevant. YAGNI for free
  • 14.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Con: Hard to modify
  • 15.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else if some other condition then some more business logic else some more business logic some more DB access Con: Hard to modify and evolve. No graceful path to richer behavior
  • 16.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Con: Hard to reuse. No rich domain model. How do you reuse this bit only?
  • 17.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Con: Hard to test Business logic is mixed up with DB access
  • 18.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Con: Hard to test How can you test just these bits?
  • 19.
    A typical transactionscript get data from DB do some business logic write some data to DB some more business logic if some condition then some more DB access some more business logic some more DB access else some more business logic some more DB access Can we fix these problems? Yes, we can!
  • 20.
    The fix: Use FunctionalProgramming & Domain Driven Design High coolness score
  • 21.
    Part II Intro toFunctional Programming Function
  • 22.
    The Tunnel of Transformation Function apple-> banana A function is a thing which transforms inputs to outputs
  • 23.
    A function isa standalone thing, not attached to a class It can be used for inputs and outputs of other functions
  • 24.
    input A function canbe an output A function is a standalone thing
  • 25.
    output A function canbe an input A function is a standalone thing
  • 26.
    input output A functioncan be a parameter A function is a standalone thing
  • 27.
  • 28.
  • 29.
    Lego Philosophy 1. Allpieces are designed to be connected 2. The pieces are reusable in many contexts 3. Connect two pieces together and get another "piece" that can still be connected
  • 30.
    All pieces aredesigned to be connected
  • 31.
    The pieces arereusable in different contexts
  • 32.
    Connect two piecestogether and get another "piece" that can still be connected
  • 33.
    Make big thingsfrom small things in the same way
  • 35.
  • 36.
    Function composition Function 1 apple-> banana Function 2 banana -> cherry
  • 37.
    Function composition >> Function 1 apple-> banana Function 2 banana -> cherry
  • 38.
    Function composition New Function apple-> cherry Can't tell it was built from smaller functions! Where did the banana go? (abstraction)
  • 39.
    Building big thingsfrom functions It's compositions all the way up
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
    Http Response Http Request Check out my"Power of Composition" talk for more details.
  • 45.
  • 46.
  • 47.
    New types arebuilt from smaller types by: Composing with “AND” Composing with “OR”
  • 48.
    Example: pairs, tuples,records FruitSalad = One each of and and Compose with “AND” type FruitSalad = { Apple: AppleVariety Banana: BananaVariety Cherry: CherryVariety }
  • 49.
    Snack = oror Compose with “OR” type Snack = | Apple of AppleVariety | Banana of BananaVariety | Cherry of CherryVariety
  • 50.
    Real world example oftype composition
  • 51.
    Example of somerequirements: We accept three forms of payment: Cash, PayPal, or Card. For Cash we don't need any extra information For PayPal we need an email address For Cards we need a card type and card number
  • 52.
    interface IPaymentMethod {..} class Cash(): IPaymentMethod {..} class PayPal(string emailAddress): IPaymentMethod {..} class Card(string cardType, string cardNo) : IPaymentMethod {..} In OO design you would probably implement it as an interface and a set of subclasses, like this:
  • 53.
    type EmailAddress =string type CardNumber = string In FP you would probably implement by composing types, like this:
  • 54.
    type EmailAddress =... type CardNumber = … type CardType = Visa | Mastercard type CreditCardInfo = { CardType : CardType CardNumber : CardNumber }
  • 55.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo
  • 56.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD
  • 57.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD type Payment = { Amount : PaymentAmount Currency: Currency Method: PaymentMethod }
  • 58.
    type EmailAddress =... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD type Payment = { Amount : PaymentAmount Currency: Currency Method: PaymentMethod }
  • 59.
    Applying FP principlesto Transaction Scripts
  • 60.
    Introducing "Workflows" A.k.a. "Transaction","Story" , "Use-case" Check out "Event Storming" for understanding business events
  • 61.
    Implementing workflows A workflowwill be implemented by a function!
  • 62.
  • 63.
    Implementing workflows We willcompose a workflow from smaller components Each component is a standalone function
  • 64.
    Part III Intro toDomain-Driven Design
  • 65.
    The DDD bookcame out just after PEAA in 2003 "Focus on the domain and domain logic rather than technology" -- Eric Evans
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
    • Agile contribution: –Rapid feedback during design • DDD contribution: – Stakeholders have a shared mental model – …which is also represented in the code How can we do design right?
  • 71.
    Key DDD principle: Communicatethe design in the code
  • 72.
    Can you reallymake code represent the domain?
  • 73.
    What some sourcecode looks like
  • 74.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = {Name:string; Hand:Hand} type Game = {Deck:Deck; Players: Player list} type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand Sharedlanguage What DDD source code *should* look like (this is F#)
  • 75.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = {Name:string; Hand:Hand} type Game = {Deck:Deck; Players: Player list} type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand * means a pair. Choose one from each type list type is built in
  • 76.
    Deal (original) Deck (remaining) Deck (on table) Card Modeling aworkflow with a function type Deal = Deck -> (Deck * Card) Input Output
  • 77.
    Pickup Card (updated) Hand (original) Hand (on table) Card Modelinga workflow with a function type PickupCard = (Hand * Card) –> Hand Input Output
  • 78.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand
  • 79.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand
  • 80.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand Can non-programmers provide useful feedback?
  • 81.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | … type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand
  • 82.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | … type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand
  • 83.
    In the realworld Suit Rank Card Hand Deck Player Deal In the code Suit Rank Card Hand Deck Player Deal
  • 84.
    In the realworld Suit Rank Card Hand Deck Player Deal In the code Suit Rank Card Hand Deck Player Deal PlayerController DeckBase AbstractCardProxyFactoryBean 
  • 85.
    module CardGame = typeSuit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | … type Card = Suit * Rank type Hand = Card list type Deck = Card list type Player = { Name:string; Hand:Hand } type Game = { Deck:Deck; Players: Player list } type Deal = Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand
  • 86.
    Key DDD principle: Communicatethe design in the code 
  • 87.
  • 88.
    Grouping functionality inthe domain A bounded context
  • 89.
    Why "Bounded Context"? •"Context" – Specialized knowledge and common language – Information taken out of context is confusing or unusable • "Bounded" – Contexts must be autonomous so they can evolve independently. – Boundaries keep you focused! No scope creep!
  • 90.
  • 91.
    Bounded context withworkflows A bounded context contains a set of related workflows
  • 92.
    Bounded context withworkflows A bounded context contains a set of related workflows/functions
  • 93.
    Part IV Reinventing theTransaction Script
  • 94.
    • FP contribution: –Transactions are functions – Build them from components using composition • Functional Domain Modeling contribution: – Use composable types for a rich domain model – The domain actions are standalone/reusable – Use autonomous bounded contexts to group and manage the workflows Reinventing the Transaction Script
  • 95.
    Pros and consof Transaction Scripts Pros • Easy to understand • Focused: All code is relevant. Cons • Hard to modify/evolve • Hard to reuse • No rich domain model • Hard to test
  • 96.
    Claim: FP-style transaction scripts areeasier to comprehend than OO domain models.
  • 97.
    FP workflow All arrowsgo left to right
  • 98.
    Example: a webbackend One directional flow, even with branching
  • 99.
    Object Oriented workflow Arrowsgo in all directions We don't design microservices this way!
  • 100.
    Real-world OO dependencygraph Multiple cyclic dependencies 
  • 101.
    Real-world FP dependencygraph All arrows go left to right  Same functionality as the OO example
  • 102.
  • 103.
    Some OO-style interfaces interfaceIRepository { Insert InsertAsync Delete DeleteAsync Update UpdateAsync CommitChanges CommitChangesAsync Query QueryByKey QueryWithFilter QueryWithSpecification Contains Count QuerySummary QuerySummaryV2 ChangePassword ChangePasswordV2 DeleteAllRows LaunchMissiles LaunchMissilesV2 } One interface that supports *every* possible workflow! Where's the Interface segregation principle?
  • 104.
    FP workflow Transaction scriptstyle functions only contain what they need. Every part is relevant. You get the ISP for free.
  • 105.
    Claim: FP-style transaction scripts canbe modified with confidence
  • 106.
    Modifying a workflow Replacea component Static type checking ensures that sub-components are used correctly Minimizing the amount of code that I touch
  • 107.
    Add features toa workflow Insert new logic Minimizing the amount of code that I touch
  • 108.
    Add branching toa workflow Add conditional logic
  • 109.
    Parameterizing a workflowfor reuse Swap out parameters for reuse as long as input and output types match
  • 110.
  • 111.
    type Deal =Deck –› (Deck * Card) type PickupCard = (Hand * Card) –› Hand A rich domain model... These functions are independent, so these workflows can still evolve independently ...used in functions
  • 112.
    Sharing rich domaincomponents Shared component Yes, complex domain logic *is* compatible with transaction scripts
  • 113.
    Claim: FP-style transaction scripts arethe natural evolution of Onion/Clean/Hexagonal Architecture
  • 114.
    Traditional layered model Achange to the way that a workflow works means that you need to touch every layer.
  • 115.
    Vertical slices Each workflowcontains all the code it needs to get its job done. When the requirements change for a workflow, only the code in that particular vertical slice needs to change.
  • 116.
    Vertical slices stretchedout Confusing! 
  • 117.
    The "onion" architecture Coredomain is pure, and all I/O is at the edges  See "Functional Core/Imperative Shell"
  • 118.
  • 119.
    Review of KeyTesting Concepts • The SUT (System Under Test) should be a unit of business value – Test transactions, not classes • Tests should apply to the boundaries of a system not its internals – Tests should be done at the transaction level • A "Unit" test means the test is isolated – That is, it produces no side effects and can be run in isolation. – A unit is not a class!
  • 120.
    In a functionaldesign, all I/O is at the edges. A FP-style transaction script
  • 121.
  • 122.
  • 123.
  • 124.
    I/O in themiddle of a workflow Keep them separate
  • 125.
    FP style transactionswork on the front end too! event-driven!
  • 126.
    MVU is aFP style approach As seen in Elm, Redux, etc
  • 127.
    MVU is aFP style approach Deterministic function Deterministic function Non-deterministic actions ("I/O")
  • 128.
  • 129.
    3 different architectures… •For monoliths: – Each bounded context is a separate module with a well-defined interface • For service-oriented architecture: – Each bounded context is a separate container • For serverless: – Each individual workflow is deployed separately
  • 134.
  • 135.
    The ReinventedTransaction Script •Most business applications can be thought of as a series of transactions. • A "Transaction Script" organizes this logic as a single function, with a deterministic core and I/O at the edges. • Each transaction will have its own, autonomous, evolvableTransaction Script
  • 136.
    In conclusion… Transaction scriptsshould be more popular • Business-focused not technology-focused • Great for agile: – "Transaction" as the unit of development • Easy to understand: dataflow is one directional • Less bloat. You get ISP andYAGNI for free!
  • 137.
    In conclusion… Problems aresolved by • FP composability • Separation of I/O New, improved transaction scripts! • Have a rich domain • Are easy to modify • Are easy to test • Are microservice and serverless friendly!
  • 138.
    "Reinventing theTransaction Script" –Slides and video will be posted at • fsharpforfunandprofit.com/transactionscript Related talks – "The Power of Composition" • fsharpforfunandprofit.com/composition – "Domain Modeling Made Functional" • fsharpforfunandprofit.com/ddd Thanks! Twitter: @ScottWlaschin