type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
} // ...
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
}
Pro...
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
}
Pro...
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
}
Pro...
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
}
Pro...
Prologue: F# can help
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEma...
Domain Driven Design
with the F# type system
ScottWlaschin
@ScottWlaschin
fsharpforfunandprofit.com
FPbridge.co.uk
/ddd
What is DDD?
Functional
Programming
Domain
Modelling
What I’m going talk about:
• Demystifying functional programming
• Functional programming for real
world applications
• F#...
Demystifying functional
programming
Why is it so hard?
Functional programming is scary
Functional programming is scary
Object oriented programming is scary
Functional programming is scary
Functional programming
for real world applications
Functional programming is...
... good for mathematical and scientific tasks
... good for complicated algorithms
... really...
Functional programming is good for...
Boring
Line Of Business
Applications
(BLOBAs)
Must haves for BLOBA development...
• Express requirements clearly
• Rapid development cycle
• High quality deliverables
•...
F# vs. C#
for Domain Driven Design
A simple immutable object
How do you implement a Value object?
Equality based on comparing all properties
PersonalName:
FirstName = "Alice"
LastName...
class PersonalName
{
public PersonalName(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = ...
class PersonalName
{
public PersonalName(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = ...
class PersonalName
{
// all the code from above, plus...
public override int GetHashCode()
{
return this.FirstName.GetHash...
type PersonalName = {FirstName:string; LastName:string}
Value object definition in F#
This page intentionally left blank
Value object definition in F# (extra code for equality)
How do you implement an Entity object?
Equality based on some sort of id
Person:
Id = 1
Name = "Alice Adams"
Person:
Id = ...
class Person
{
public Person(int id, PersonalName name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; private s...
class Person
{
// all the code from above, plus...
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
pub...
[<CustomEquality; NoComparison>]
type Person = {Id:int; Name:PersonalName} with
override this.GetHashCode() = hash this.Id...
Entity object definition in F# with no equality allowed
[<CustomEquality; NoComparison>]
type Person = {Id:int; Name:Perso...
type Person = { ... ... ... }
let tryCreatePerson name =
// validate on construction
// if input is valid return something...
class PersonalName
{
public PersonalName(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = ...
[<StructuralEquality;NoComparison>]
type PersonalName = {
FirstName : string;
LastName : string }
Reviewing the F# code so...
Comparing C# vs. F#
C# F#
Value objects? Non-trivial Easy
Entity objects? Non-trivial Easy
Value objects by default? No Ye...
F# for Domain Driven Design
Communicating a domain model
Communication is hard...
U-N-I-O-N-I-Z-EU-N-I-O-N-I-Z-EU-N-I-O-N-I-Z-E
Communication in DDD: “Bounded Context”
Business Chemistry
un-ionizeunionize
Supermarket Email System
SpamSpam
Communication in DDD: “Bounded Context”
Business Chemistry
un-ionizeunionize
Supermarket Email System
SpamSpam
Sales Wareh...
Communication in DDD: “Bounded Context”
Business Chemistry
un-ionizeunionize
Supermarket Email System
SpamSpam
Sales Wareh...
Communication in DDD: “Ubiquitous Language”
Chemistry
Ion Atom Molecule Polymer Compound Bond
Communication in DDD: “Ubiquitous Language”
Chemistry
Ion Atom Molecule Polymer Compound Bond
Sales
Product Promotion Cust...
Communication in DDD: “Ubiquitous Language”
Chemistry
Ion Atom Molecule Polymer Compound Bond
Sales
Product Promotion Cust...
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| ...
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| ...
module CardGame =
type Suit = Club | Diamond | Spade | Heart
type Rank = Two |Three | Four | Five | Six | Seven | Eight
| ...
Understanding the F# type system
An introduction to “algebraic” typesAn introduction to “algebraic” types
Composable types
Creating new types
New types are constructed by combining other
types using two basic operations:
type typeW = typeX "time...
Creating new types
(a function)AddOneAddOne
int –› int
1
2
3
4
2
3
4
5
Representing pairs
AddPair
? –› int
(1,2)
(2,3)
(3,4)
(4,5)
3
5
7
9
Representing pairs
×=
(1,2)
(2,3)
(3,4)
(4,5)
1
2
3
4
2
3
4
5
Representing pairs
×=
(true, false)
(true, true)
(false, false)
(false, true)
true
false
true
false
Representing pairs
pair of ints
written int * int
pair of bools
written bool * bool
Using tuples for data
×=
Alice, Jan 12th
Bob, Feb 2nd
Carol, Mar 3rd
Set of
people
Set of
dates
Using tuples for data
×=
Alice, Jan 12th
Bob, Feb 2nd
Carol, Mar 3rd
Set of
people
Set of
dates
type Birthday = Person * D...
Representing a choice
Temp F
IsFever
? –› bool
true
false
Temp C
or
Representing a choice
+=
98˚ F
99˚ F
100˚ F
101˚ F
37.0˚ C
37.5˚ C
38.0˚ C
38.5˚ C
Temp F
Temp C
or
Representing a choice
+=
98˚ F
99˚ F
100˚ F
101˚ F
37.0˚ C
37.5˚ C
38.0˚ C
38.5˚ C
Temp F
Temp C
or
Tag these with “C”
typ...
Using choices for data
type PaymentMethod =
| Cash
| Cheque of int
| Card of CardType * CardNumber
Working with a choice type
type PaymentMethod =
| Cash
| Cheque of int
| Card of CardType * CardNumber
let printPayment me...
Using choices vs. inheritance
interface IPaymentMethod {..}
class Cash : IPaymentMethod {..}
class Cheque : IPaymentMethod...
Summary: What are types for in FP?
An annotation to a value for type checking
type AddOne: int –› int
Domain modelling too...
TYPE ALL THE THINGS
Designing with types
What can we do with this type system?
Required vs. Optional
type PersonalName =
{
FirstName: string;
MiddleInitial: string;
LastName: string;
}
required
require...
Null is not the same as “optional”
Length
string –› int
“a”
“b”
“c”
1
2
3
“a”
“b”
“c”
null
Spock, set
phasers to null!
That is illogical,
Captain
Null is not the same as “optional”
Length
string –› int
“a”
“b”
“c”
null
1
2
3
Null is not allowed
Length
string –› int
“a”
“b”
“c”
null
1
2
3
X
A better way for optional values
+=
“a”
“b”
“c”
“a”
“b”
“c”
missing
or
Tag with “Nothing”
type OptionalString =
| SomeStri...
type OptionalInt =
| SomeInt of int
| Nothing
type OptionalString =
| SomeString of string
| Nothing
type OptionalBool =
|...
The built-in “Option” type
type PersonalName =
{
FirstName: string
MiddleInitial: string
LastName: string
}
type Option<'T...
The built-in “Option” type
type PersonalName =
{
FirstName: string
MiddleInitial: Option<string>
LastName: string
}
type O...
The built-in “Option” type
type PersonalName =
{
FirstName: string
MiddleInitial: string option
LastName: string
}
type Op...
Single choice types
type Something =
| ChoiceA of A
type Email =
| Email of string
type CustomerId =
| CustomerId of int
Wrapping primitive types
Is an EmailAddress just a string?
Is a CustomerId just a int?
Use single choice types to keep the...
Creating the EmailAddress type
let createEmailAddress (s:string) =
if Regex.IsMatch(s,@"^S+@S+.S+$")
then (EmailAddress s)...
Constrained strings
type String50 = String50 of string
let createString50 (s:string) =
if s.Length <= 50
then Some (String...
Constrained numbers
+– 999999Qty: Add To Cart
Constrained numbers
type OrderLineQty = OrderLineQty of int
let createOrderLineQty qty =
if qty >0 && qty <= 99
then Some ...
type Contact = {
FirstName: string
MiddleInitial: string
LastName: string
EmailAddress: string
IsEmailVerified: bool
}
The...
The challenge, revisited
type Contact = {
FirstName: string
MiddleInitial: string option
LastName: string
EmailAddress: st...
The challenge, revisited
type Contact = {
FirstName: String50
MiddleInitial: String1 option
LastName: String50
EmailAddres...
type Contact = {
Name: PersonalName
Email: EmailContactInfo }
The challenge, revisited
type PersonalName = {
FirstName: St...
Encoding domain logic
Rule 1: If the email is changed, the verified flag
must be reset to false.
Rule 2: The verified flag...
Encoding domain logic
type VerifiedEmail =VerifiedEmail of EmailAddress
type EmailContactInfo =
| Unverified of EmailAddre...
type EmailAddress = ...
type VerifiedEmail =
VerifiedEmail of EmailAddress
type EmailContactInfo =
| Unverified of EmailAd...
Making illegal states unrepresentable
type Contact = {
Name: Name
Email: EmailContactInfo
Address: PostalContactInfo
}
Making illegal states unrepresentable
type Contact = {
Name: Name
Email: EmailContactInfo
Address: PostalContactInfo
}
New...
Making illegal states unrepresentable
type Contact = {
Name: Name
Email: EmailContactInfo option
Address: PostalContactInf...
Making illegal states unrepresentable
“A contact must have an email or a postal address”
implies:
• email address only, or...
Making illegal states unrepresentable
type ContactInfo =
| EmailOnly of EmailContactInfo
| AddrOnly of PostalContactInfo
|...
Making illegal states unrepresentable
type Contact = {
Name: Name
Email: EmailContactInfo
Address: PostalContactInfo
}
typ...
Making illegal states unrepresentable
type Contact = {
Name: Name
PrimaryContactInfo: ContactInfo
SecondaryContactInfo: Co...
States and Transitions
Modelling a common scenario
States and transitions
State A State B State C
Transition from A to B
States and transitions
Transition from B to A
Transi...
States and transitions
Unverified
EmailAddress
Verified
EmailAddress
Verified
States and transitions for email address
Rul...
States and transitions
Undelivered
Out for
delivery
Delivered
Put on truck
Address not found
Signed for
States and transit...
States and transitions
Empty Cart Active Cart Paid Cart
Add Item
Remove Item
Pay
Add Item
Remove Item
States and transitio...
States and transitions
Empty Cart Active Cart Paid Cart
Add Item
Remove Item
Pay
Add Item
Remove Item
States and transitio...
Modelling the shopping cart example
type ActiveCartData =
{ UnpaidItems: Item list }
type PaidCartData =
{ PaidItems: Item...
Shopping cart example
initCart :
Item –› ShoppingCart
addToActive:
(ActiveCartData * Item) –› ShoppingCart
removeFromActiv...
Shopping cart example
let initCart item =
{ UnpaidItems=[item] }
let addToActive (cart:ActiveCart) item =
{ cart with Unpa...
Shopping cart example
Client code to add an item using the API
let addItem cart item =
match cart with
| EmptyCart –›
init...
Shopping cart example
let removeFromActive (cart:ActiveCart) item =
let remainingItems =
removeFromList cart.existingItems...
Shopping cart example
Client code to remove an item using the API
let removeItem cart item =
match cart with
| EmptyCart –...
Why design with state transitions?
• Each state can have different allowable data.
• All states are explicitly documented....
Review
What I covered in this talk:
• Ubiquitous language
– Self-documenting designs
• Algebraic types
– products and sums...
Stuff I haven’t had time to cover:
• Services
• CQRS
• The functional approach to use cases
• Domain events
• Error handli...
F# is low risk
F# is the safe choice for functional-first development
credit: @7sharp9 @MattDrivenDev
Enterprise
developme...
Domain Driven Design with the F# type system
DDD in F# resources
fsharpforfunandprofit.com/ddd
gorodinski.com
tomasp.net/b...
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Upcoming SlideShare
Loading in …5
×

Domain Driven Design with the F# type System -- F#unctional Londoners 2014

43,461
-1

Published on

(Video of these slides here http://fsharpforfunandprofit.com/ddd)

Statically typed functional programming languages like F# encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers.

Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time.

In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.

Code, links to video, etc., at http://fsharpforfunandprofit.com/ddd

NEW AND IMPROVED - added sections on:
* why OO, not FP is scary
* designing with states and transitions

Published in: Technology
0 Comments
39 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
43,461
On Slideshare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
254
Comments
0
Likes
39
Embeds 0
No embeds

No notes for slide

Domain Driven Design with the F# type System -- F#unctional Londoners 2014

  1. 1. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } // true if ownership of // email address is confirmed Domain Driven Design with the F# type system Prologue: how many things are wrong?
  2. 2. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } Prologue: which values are optional?
  3. 3. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } Prologue: what are the constraints?
  4. 4. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } Prologue: what groups are atomic?
  5. 5. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } Prologue: domain logic?
  6. 6. Prologue: F# can help type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool }
  7. 7. Domain Driven Design with the F# type system ScottWlaschin @ScottWlaschin fsharpforfunandprofit.com FPbridge.co.uk /ddd
  8. 8. What is DDD?
  9. 9. Functional Programming Domain Modelling
  10. 10. What I’m going talk about: • Demystifying functional programming • Functional programming for real world applications • F# vs. C# for domain driven design • Understanding the F# type system • Designing with types
  11. 11. Demystifying functional programming Why is it so hard?
  12. 12. Functional programming is scary
  13. 13. Functional programming is scary
  14. 14. Object oriented programming is scary
  15. 15. Functional programming is scary
  16. 16. Functional programming for real world applications
  17. 17. Functional programming is... ... good for mathematical and scientific tasks ... good for complicated algorithms ... really good for parallel processing ... but you need a PhD in computer science 
  18. 18. Functional programming is good for... Boring Line Of Business Applications (BLOBAs)
  19. 19. Must haves for BLOBA development... • Express requirements clearly • Rapid development cycle • High quality deliverables • Fun
  20. 20. F# vs. C# for Domain Driven Design A simple immutable object
  21. 21. How do you implement a Value object? Equality based on comparing all properties PersonalName: FirstName = "Alice" LastName = "Adams" PersonalName: FirstName = "Alice" LastName = "Adams"Equal Therefore must be immutable
  22. 22. class PersonalName { public PersonalName(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get; private set; } public string LastName { get; private set; } } Value object definition in C#
  23. 23. class PersonalName { public PersonalName(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get; private set; } public string LastName { get; private set; } } Value object definition in C#
  24. 24. class PersonalName { // all the code from above, plus... public override int GetHashCode() { return this.FirstName.GetHashCode() + this.LastName.GetHashCode(); } public override bool Equals(object other) { return Equals(other as PersonalName); } public bool Equals(PersonalName other) { if ((object) other == null) { return false; } return FirstName == other.FirstName && LastName == other.LastName; } Value object definition in C# (extra code for equality)
  25. 25. type PersonalName = {FirstName:string; LastName:string} Value object definition in F#
  26. 26. This page intentionally left blank Value object definition in F# (extra code for equality)
  27. 27. How do you implement an Entity object? Equality based on some sort of id Person: Id = 1 Name = "Alice Adams" Person: Id = 1 Name = "Bilbo Baggins" Equal X  Generally has mutable content
  28. 28. class Person { public Person(int id, PersonalName name) { this.Id = id; this.Name = name; } public int Id { get; private set; } public PersonalName Name { get; set; } } Entity object definition in C# (part 1)
  29. 29. class Person { // all the code from above, plus... public override int GetHashCode() { return this.Id.GetHashCode(); } public override bool Equals(object other) { return Equals(other as Person); } public bool Equals(Person other) { if ((object) other == null) { return false; } return Id == other.Id; } } Entity object definition in C# (part 2)
  30. 30. [<CustomEquality; NoComparison>] type Person = {Id:int; Name:PersonalName} with override this.GetHashCode() = hash this.Id override this.Equals(other) = match other with | :? Person as p -> (this.Id = p.Id) | _ -> false Entity object definition in F# with equality override
  31. 31. Entity object definition in F# with no equality allowed [<CustomEquality; NoComparison>] type Person = {Id:int; Name:PersonalName} [<NoEquality; NoComparison>]
  32. 32. type Person = { ... ... ... } let tryCreatePerson name = // validate on construction // if input is valid return something // if input is not valid return error   Advantages of immutability
  33. 33. class PersonalName { public PersonalName(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get; private set; } public string LastName { get; private set; } public override int GetHashCode() { return this.FirstName.GetHashCode() + this.LastName.GetHashCode(); } public override bool Equals(object other) { return Equals(other as PersonalName); } public bool Equals(PersonalName other) { if ((object) other == null) { return false; } return FirstName == other.FirstName && LastName == other.LastName; } } Reviewing the C# code so far... class Person { public Person(int id, PersonalName name) { this.Id = id; this.Name = name; } public int Id { get; private set; } public PersonalName Name { get; set; } public override int GetHashCode() { return this.Id.GetHashCode(); } public override bool Equals(object other) { return Equals(other as Person); } public bool Equals(Person other) { if ((object) other == null) { return false; } return Id == other.Id; } } : IValue : IEntity
  34. 34. [<StructuralEquality;NoComparison>] type PersonalName = { FirstName : string; LastName : string } Reviewing the F# code so far... [<NoEquality; NoComparison>] type Person = { Id : int; Name : PersonalName }
  35. 35. Comparing C# vs. F# C# F# Value objects? Non-trivial Easy Entity objects? Non-trivial Easy Value objects by default? No Yes Immutable objects by default? No Yes Can you tellValue objects from Entities at a glance? No Yes Understandable by non-programmer? No Yes C# vs. F# for DDD
  36. 36. F# for Domain Driven Design Communicating a domain model
  37. 37. Communication is hard... U-N-I-O-N-I-Z-EU-N-I-O-N-I-Z-EU-N-I-O-N-I-Z-E
  38. 38. Communication in DDD: “Bounded Context” Business Chemistry un-ionizeunionize Supermarket Email System SpamSpam
  39. 39. Communication in DDD: “Bounded Context” Business Chemistry un-ionizeunionize Supermarket Email System SpamSpam Sales Warehouse ProductProduct
  40. 40. Communication in DDD: “Bounded Context” Business Chemistry un-ionizeunionize Supermarket Email System SpamSpam Sales Warehouse ProductProduct Marketing Finance CustomerCustomer
  41. 41. Communication in DDD: “Ubiquitous Language” Chemistry Ion Atom Molecule Polymer Compound Bond
  42. 42. Communication in DDD: “Ubiquitous Language” Chemistry Ion Atom Molecule Polymer Compound Bond Sales Product Promotion Customer Tracking
  43. 43. Communication in DDD: “Ubiquitous Language” Chemistry Ion Atom Molecule Polymer Compound Bond Sales Product Promotion Customer Tracking Warehouse Product Stock Transfer Depot Tracking
  44. 44. module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King | Ace 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 Ubiquitouslanguage list type is built in
  45. 45. module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King | Ace 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
  46. 46. module CardGame = type Suit = Club | Diamond | Spade | Heart type Rank = Two |Three | Four | Five | Six | Seven | Eight | Nine |Ten | Jack | Queen | King | Ace 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
  47. 47. Understanding the F# type system An introduction to “algebraic” typesAn introduction to “algebraic” types
  48. 48. Composable types
  49. 49. Creating new types New types are constructed by combining other types using two basic operations: type typeW = typeX "times" typeY type typeZ = typeX "plus" typeY
  50. 50. Creating new types (a function)AddOneAddOne int –› int 1 2 3 4 2 3 4 5
  51. 51. Representing pairs AddPair ? –› int (1,2) (2,3) (3,4) (4,5) 3 5 7 9
  52. 52. Representing pairs ×= (1,2) (2,3) (3,4) (4,5) 1 2 3 4 2 3 4 5
  53. 53. Representing pairs ×= (true, false) (true, true) (false, false) (false, true) true false true false
  54. 54. Representing pairs pair of ints written int * int pair of bools written bool * bool
  55. 55. Using tuples for data ×= Alice, Jan 12th Bob, Feb 2nd Carol, Mar 3rd Set of people Set of dates
  56. 56. Using tuples for data ×= Alice, Jan 12th Bob, Feb 2nd Carol, Mar 3rd Set of people Set of dates type Birthday = Person * Date
  57. 57. Representing a choice Temp F IsFever ? –› bool true false Temp C or
  58. 58. Representing a choice += 98˚ F 99˚ F 100˚ F 101˚ F 37.0˚ C 37.5˚ C 38.0˚ C 38.5˚ C Temp F Temp C or
  59. 59. Representing a choice += 98˚ F 99˚ F 100˚ F 101˚ F 37.0˚ C 37.5˚ C 38.0˚ C 38.5˚ C Temp F Temp C or Tag these with “C” typeTemp = | F of int | C of float
  60. 60. Using choices for data type PaymentMethod = | Cash | Cheque of int | Card of CardType * CardNumber
  61. 61. Working with a choice type type PaymentMethod = | Cash | Cheque of int | Card of CardType * CardNumber let printPayment method = match method with | Cash –› printfn “Paid in cash" | Cheque checkNo –› printfn “Paid by cheque: %i" checkNo | Card (cardType,cardNo) –› printfn “Paid with %A %A" cardType cardNo
  62. 62. Using choices vs. inheritance interface IPaymentMethod {..} class Cash : IPaymentMethod {..} class Cheque : IPaymentMethod {..} class Card : IPaymentMethod {..} type PaymentMethod = | Cash | Cheque of int | Card of CardType * CardNumber class Evil : IPaymentMethod {..} Data and code is scattered around many locations What goes in here? What is the common behaviour? OO version:
  63. 63. Summary: What are types for in FP? An annotation to a value for type checking type AddOne: int –› int Domain modelling tool type Deal = Deck –› (Deck * Card)
  64. 64. TYPE ALL THE THINGS
  65. 65. Designing with types What can we do with this type system?
  66. 66. Required vs. Optional type PersonalName = { FirstName: string; MiddleInitial: string; LastName: string; } required required optional
  67. 67. Null is not the same as “optional” Length string –› int “a” “b” “c” 1 2 3 “a” “b” “c” null
  68. 68. Spock, set phasers to null! That is illogical, Captain
  69. 69. Null is not the same as “optional” Length string –› int “a” “b” “c” null 1 2 3
  70. 70. Null is not allowed Length string –› int “a” “b” “c” null 1 2 3 X
  71. 71. A better way for optional values += “a” “b” “c” “a” “b” “c” missing or Tag with “Nothing” type OptionalString = | SomeString of string | Nothing
  72. 72. type OptionalInt = | SomeInt of int | Nothing type OptionalString = | SomeString of string | Nothing type OptionalBool = | SomeBool of bool | Nothing Defining optional types
  73. 73. The built-in “Option” type type PersonalName = { FirstName: string MiddleInitial: string LastName: string } type Option<'T> = | Some of 'T | None
  74. 74. The built-in “Option” type type PersonalName = { FirstName: string MiddleInitial: Option<string> LastName: string } type Option<'T> = | Some of 'T | None
  75. 75. The built-in “Option” type type PersonalName = { FirstName: string MiddleInitial: string option LastName: string } type Option<'T> = | Some of 'T | None
  76. 76. Single choice types type Something = | ChoiceA of A type Email = | Email of string type CustomerId = | CustomerId of int
  77. 77. Wrapping primitive types Is an EmailAddress just a string? Is a CustomerId just a int? Use single choice types to keep them distinct type EmailAddress = EmailAddress of string type PhoneNumber = PhoneNumber of string type CustomerId = CustomerId of int type OrderId = OrderId of int
  78. 78. Creating the EmailAddress type let createEmailAddress (s:string) = if Regex.IsMatch(s,@"^S+@S+.S+$") then (EmailAddress s) else ? let createEmailAddress (s:string) = if Regex.IsMatch(s,@"^S+@S+.S+$") then Some (EmailAddress s) else None createEmailAddress: string –› EmailAddress createEmailAddress: string –› EmailAddress option
  79. 79. Constrained strings type String50 = String50 of string let createString50 (s:string) = if s.Length <= 50 then Some (String50 s) else None createString50 : string –› String50 option
  80. 80. Constrained numbers +– 999999Qty: Add To Cart
  81. 81. Constrained numbers type OrderLineQty = OrderLineQty of int let createOrderLineQty qty = if qty >0 && qty <= 99 then Some (OrderLineQty qty) else None createOrderLineQty: int –› OrderLineQty option
  82. 82. type Contact = { FirstName: string MiddleInitial: string LastName: string EmailAddress: string IsEmailVerified: bool } The challenge, revisited
  83. 83. The challenge, revisited type Contact = { FirstName: string MiddleInitial: string option LastName: string EmailAddress: string IsEmailVerified: bool }
  84. 84. The challenge, revisited type Contact = { FirstName: String50 MiddleInitial: String1 option LastName: String50 EmailAddress: EmailAddress IsEmailVerified: bool }
  85. 85. type Contact = { Name: PersonalName Email: EmailContactInfo } The challenge, revisited type PersonalName = { FirstName: String50 MiddleInitial: String1 option LastName: String50 } type EmailContactInfo = { EmailAddress: EmailAddress IsEmailVerified: bool }
  86. 86. Encoding domain logic Rule 1: If the email is changed, the verified flag must be reset to false. Rule 2: The verified flag can only be set by a special verification service type EmailContactInfo = { EmailAddress: EmailAddress IsEmailVerified: bool }
  87. 87. Encoding domain logic type VerifiedEmail =VerifiedEmail of EmailAddress type EmailContactInfo = | Unverified of EmailAddress | Verified ofVerifiedEmail type VerificationService = (EmailAddress * VerificationHash) –› VerifiedEmail option
  88. 88. type EmailAddress = ... type VerifiedEmail = VerifiedEmail of EmailAddress type EmailContactInfo = | Unverified of EmailAddress |Verified ofVerifiedEmail The challenge, completed type PersonalName = { FirstName: String50 MiddleInitial: String1 option LastName: String50 } type Contact = { Name: PersonalName Email: EmailContactInfo }
  89. 89. Making illegal states unrepresentable type Contact = { Name: Name Email: EmailContactInfo Address: PostalContactInfo }
  90. 90. Making illegal states unrepresentable type Contact = { Name: Name Email: EmailContactInfo Address: PostalContactInfo } New rule: “A contact must have an email or a postal address”
  91. 91. Making illegal states unrepresentable type Contact = { Name: Name Email: EmailContactInfo option Address: PostalContactInfo option } New rule: “A contact must have an email or a postal address”
  92. 92. Making illegal states unrepresentable “A contact must have an email or a postal address” implies: • email address only, or • postal address only, or • both email address and postal address
  93. 93. Making illegal states unrepresentable type ContactInfo = | EmailOnly of EmailContactInfo | AddrOnly of PostalContactInfo | EmailAndAddr of EmailContactInfo * PostalContactInfo type Contact = { Name: Name ContactInfo : ContactInfo } “A contact must have an email or a postal address”
  94. 94. Making illegal states unrepresentable type Contact = { Name: Name Email: EmailContactInfo Address: PostalContactInfo } type Contact = { Name: Name ContactInfo : ContactInfo } type ContactInfo = | EmailOnly of EmailContactInfo | AddrOnly of PostalContactInfo | EmailAndAddr of EmailContactInfo * PostalContactInfo AFTER: Email and address merged into one type “A contact must have an email or a postal address” BEFORE: Email and address separate
  95. 95. Making illegal states unrepresentable type Contact = { Name: Name PrimaryContactInfo: ContactInfo SecondaryContactInfo: ContactInfo option } “A contact must have an email or a postal address”“A contact must have at least one way of being contacted” type ContactInfo = | Email of EmailContactInfo | Addr of PostalContactInfo
  96. 96. States and Transitions Modelling a common scenario
  97. 97. States and transitions State A State B State C Transition from A to B States and transitions Transition from B to A Transition from B to C
  98. 98. States and transitions Unverified EmailAddress Verified EmailAddress Verified States and transitions for email address Rule: "You can't send a verification message to a verified email" Rule: "You can't send a password reset message to a unverified email "
  99. 99. States and transitions Undelivered Out for delivery Delivered Put on truck Address not found Signed for States and transitions for shipments Rule: "You can't put a package on a truck if it is already out for delivery" Rule: "You can't sign for a package that is already delivered"
  100. 100. States and transitions Empty Cart Active Cart Paid Cart Add Item Remove Item Pay Add Item Remove Item States and transitions for shopping cart Rule: "You can't remove an item from an empty cart" Rule: "You can't change a paid cart" Rule: "You can't pay for a cart twice"
  101. 101. States and transitions Empty Cart Active Cart Paid Cart Add Item Remove Item Pay Add Item Remove Item States and transitions for shopping cart type ActiveCartData = { UnpaidItems: Item list } type PaidCartData = { PaidItems: Item list; Payment: Payment } no data needed
  102. 102. Modelling the shopping cart example type ActiveCartData = { UnpaidItems: Item list } type PaidCartData = { PaidItems: Item list; Payment: Payment} type ShoppingCart = | EmptyCart // no data | ActiveCart of ActiveCartData | PaidCart of PaidCartData Empty Cart Active Cart Paid Cart Add Item Remove Item Pay Add Item Remove Item
  103. 103. Shopping cart example initCart : Item –› ShoppingCart addToActive: (ActiveCartData * Item) –› ShoppingCart removeFromActive: (ActiveCartData * Item) –› ShoppingCart pay: (ActiveCartData * Payment) –› ShoppingCart Shopping Cart API Empty Cart Active Cart Paid Cart Add Item Remove Item Pay Add Item Remove Item
  104. 104. Shopping cart example let initCart item = { UnpaidItems=[item] } let addToActive (cart:ActiveCart) item = { cart with UnpaidItems = item :: cart.existingItems } Server code to add an item
  105. 105. Shopping cart example Client code to add an item using the API let addItem cart item = match cart with | EmptyCart –› initCart item | ActiveCart activeData –› addToActive(activeData,item) | PaidCart paidData –› ???
  106. 106. Shopping cart example let removeFromActive (cart:ActiveCart) item = let remainingItems = removeFromList cart.existingItems item match remainingItems with | [ ] –› EmptyCart | _ –› {cart with UnpaidItems = remainingItems} Server code to remove an item
  107. 107. Shopping cart example Client code to remove an item using the API let removeItem cart item = match cart with | EmptyCart –› ??? | ActiveCart activeData –› removeFromActive(activeData,item) | PaidCart paidData –› ???
  108. 108. Why design with state transitions? • Each state can have different allowable data. • All states are explicitly documented. • All transitions are explicitly documented. • It is a design tool that forces you to think about every possibility that could occur. Undelivered Out for delivery Delivered Put on truck Address not found Signed for
  109. 109. Review What I covered in this talk: • Ubiquitous language – Self-documenting designs • Algebraic types – products and sums • Designing with types – Options instead of null – Single case unions – Choices rather than inheritance – Making illegal states unrepresentable • States and transitions
  110. 110. Stuff I haven’t had time to cover: • Services • CQRS • The functional approach to use cases • Domain events • Error handling • And much more...
  111. 111. F# is low risk F# is the safe choice for functional-first development credit: @7sharp9 @MattDrivenDev Enterprise development F# on over 2 billion devices Mobile development Need to persuade your manager? -> FPbridge.co.uk/why-fsharp.html
  112. 112. Domain Driven Design with the F# type system DDD in F# resources fsharpforfunandprofit.com/ddd gorodinski.com tomasp.net/blog/type-first-development.aspx/ #fsharp on Twitter Contact me @ScottWlaschin FPbridge.co.uk
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×