class Contact {
string Firstname;
string MiddleInitial;
string LastName;
string EmailAdress;
bool IsEmailVerified;
}
How many things are
wrong with this design?
class Contact {
string Firstname;
string MiddleInitial;
string LastName;
string EmailAdress;
bool IsEmailVerified;
}
Which values are optional?
What are the constraints?
Which fields are linked?
What is the domain logic?
Your type system may be able to help with these questions!
Domain Driven Design
with your Typesystem

»Focus on the domain and
domain logic rather than
technology«
- Eric Evans
Value objects
public class PersonalName {
private String firstName;
private String lastName;
public PersonalName(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getLastName() {
return lastName;
}
public String getFirstName() {
return firstName;
}
}
In Java or C#
public class PersonalName {
// All the code from above, plus...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonalName that = (PersonalName) o;
if (!firstName.equals(that.firstName)) return false;
if (!lastName.equals(that.lastName)) return false;
return true;
}
@Override
public int hashCode() {
int result = firstName.hashCode();
result = 31 * result + lastName.hashCode();
return result;
}
}
In Java or C#
is this a reasonable amount of code?
case class PersonalName(
firstName : String,
lastName : String
)
In Scala
type PersonalName = {
firstName : string,
lastName : string
}
In F#
this page was intentionally left blank
Entities
public class Person {
private int id;
private PersonalName personalName;
public Person(int id, PersonalName personalName) {
this.id = id;
this.personalName = personalName;
}
public int getId() {
return id;
}
public PersonalName getPersonalName() {
return personalName;
}
public void setPersonalName(PersonalName personalName) {
this.personalName = personalName;
}
}
In Java or C#
public class PersonalName {
// All the code from above, plus...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (id != person.id) return false;
return true;
}
@Override
public int hashCode() {
return id;
}
}
In Java or C#
Now compares on Id
case class PersonalName(id : Int)(
val firstName : String,
val lastName : String
)
In Scala
[<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
In F#
[<NoEquality; NoComparison>]
type Person = { Id : int; Name : PersonalName }
In F#
Scala & F# for Domain Driven Design
Communicating a Domain Model
Communication is hard!
U-N-I-O-N-I-Z-E


Supermarket
Spam
Email system
Spam
Bounded context
Sales
Product
Warehouse
Product
Bounded context
Marketing
Customer
Finance
Customer
Bounded context
Chemistry
Ubiquitous Language
Ion Atom Molecule
Polymer Compound
Bond
Sales
Ubiquitous Language
Product Promotion
Customer Tracking
Warehouse
Ubiquitous Language
Product Stock Transfer
Depot Tracking
Understanding algebraic types
composable types
Product types
Alice, Jan 12th
Bob, Feb 2nd
=
Set of
People
Set of
Dates
x
type Birthday = Person * Date
Sum types
=
Set of
Integers
Set of
Floats
+
abstract class Temp
case class F(val : int) extends Temp
case class C(val : Float) extends Temp
Temp
Fahrenheit
Temp
Celcius
or
Using choices for data
type PaymentMethod =
| Cash
| Cheque of int
| Card of CardType * CardNumber
Working with a choice type
let printPayment = function
| Cash -> printfn "Paid in cash"
| Cheque checkNo -> printfn "Paid by cheque: %i" checkNo
| Card (type, num) -> printfn "Paid with %A %A" cardType cardNo
What are types for in
Functional Programming
An annotation to a value for type checking
type AddOne = Int => Int
Domain modeling tool!
type Deal = Deck => (Deck, Card)
a good static type system is like having compile type tests
Designing with types
What can we do with the type system?
case class PersonalName(
firstName : String,
middleInitial : String,
lastName : String
)
Required vs Optional
required
required
optional
How can we represent optional values?
"a"
"b"
"c"
null
Length
String => Int
1
2
3
Null is not the same as "optional"
null is the Saruman of static typing!
A better way to represent nothing
=
+
Tag these with
"SomeString"
"a"
"b"
"c"
Missing
or
"a"
"b"
"c"
Tag with
"Nothing"
type OptionalString = SomeString of String | Nothing
The built in option type
abstract class Option[A]
case class Some[A](a : A) extends Option[A]
case object None extends Option[Nothing]
type Option<'T> =
| Some of 'T
| None
case class PersonalName(
firstName : String,
middleInitial : Option[String]
lastName : String
)
Single choice types
case class EmailAddress(email : String)
type CustomerId = CustomerId of Int
case class PhoneNumber(number : String)
distinct types!
type OrderId = OrderId of int
Creating an EmailAddress instance
let createEmailAddress (s : string) =
if Regex.Match(s, @"^\S+@\S+\.\S+$")
then Some(EmailAddress s)
else None
createEmailAddress:
string -> Option<EmailAddress>
Constrained strings
let createString50 (s : string) =
if s.Length <= 50
then Some(String50 s)
else None
createString50:
string -> Option<String50>
The challenge, revised
case class Contact(
name : PersonalName,
email : EmailContactInfo
)
case class PersonalName(
firstName : String50,
middleInitial : Option[String1],
lastName : String50
)
case class EmailContactInfo(
emailAddress : EmailAddress,
isEmailVerified : boolean
)
Encoding domain logic
case class EmailContactInfo(
emailAddress : EmailAddress,
isEmailVerified : boolean
)
anyone can set this to true
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
Encoding domain logic
type VerifiedEmail = VerifiedEmail of EmailAddress
type EmailContactInfo =
| Unverified of EmailAddress
| Verified of VerifiedEmail
type VerificationService =
(EmailAddress * VerificationHash) -> Option<VerifiedEmail>
The challenge completed!
type VerifiedEmail = VerifiedEmail of EmailAddress
type EmailContactInfo =
| Unverified of EmailAddress
| Verified of VerifiedEmail
type Contact = {
Name : PersonalName,
Email : EmailContactInfo
}
type PersonalName = {
FirstName : String50,
MiddleInitial : Option<String1>,
LastName : String50
}
type EmailAddress = EmailAddress of string
The ubiquitous language is
evolving along with the design
Making illegal states unrepresentable
type Contact = {
Name : PersonalName,
Email : EmailContactInfo,
Address : PostalContactInfo
}
added some time later
Making illegal states unrepresentable
type Contact = {
Name : PersonalName,
Email : EmailContactInfo,
Address : PostalContactInfo
}
Doesn't meet new requirements
New rule: "A contact must have an email or a postal address"
Making illegal states unrepresentable
type Contact = {
Name : PersonalName,
Email : Option<EmailContactInfo>,
Address : Option<PostalContactInfo>
}
Doesn't meet new requirements either!
New rule: "A contact must have an email or a postal address"
»Make illegal states unrepresentable«
- Yaron Minsky
Making illegal states unrepresentable
implies:
- email address only
- postal address only
- both email and postal address
only three possibilities
New rule: "A contact must have an email or a postal address"
Making illegal states unrepresentable
type ContactInfo =
| EmailOnly of EmailContactInfo
| AddrOnly of PostalContactInfo
| EmailAndAdrr of EmailContactInfo * PostalContactInfo
only three possibilities
New rule: "A contact must have an email or a postal address"
requirements are now encoded in the types!
type Contact = {
Name : PersonalName
ContactInfo : ContactInfo
}
Static types are
almost as
awesome as
this!
Making illegal states unrepresentable
type ContactInfo =
| Email of EmailContactInfo
| Postal of PostalContactInfo
one way of being contacted is required
New rule: "A contact must have an email or a postal address"
Is this really what the business wants?
type Contact = {
Name : Name
PrimaryContactInfo : ContactInfo
SecondaryContactInfo : Option<ContactInfo>
}
States and Transitions
Modelling a common scenario
State A
State B
State C
Transition from A to B
Transition from B to A
Transition from B to C
Unverified EmailAddress
Verified
Verified EmailAddress
Rule: You can't send a verification message to a verified email
Rule: You can't send a password reset message to an unverified email
States and transitions for email address
Empty
Active
Paid
Add Item
Remove Item
Pay
States and transitions for shopping carts

Add Item

Remove Item
Rule: You can't remove an item from an empty cart
Rule: You can't charge a paid cart
Rule: You can't pay for a cart twice
Empty
Active
Paid
Add Item
Remove Item
Pay
States and transitions for shopping carts

Add Item

Remove Item
no data needed
type ActiveCartData = {
UnpaidItems : Item list
}
type PaidCartData = {
PaidItems : Item list
Payment : Payment
}
What data do we need to store?
Modelling the shopping cart
type ActiveCartData = {
UnpaidItems : Item list
}
type PaidCartData = {
PaidItems : Item list
Payment : Payment
}
type ShoppingCart =
| EmptyCart
| ActiveCart of ActiveCartData
| PaidCart of PaidCartData
one of three states
Modelling the shopping cart: Server API
initCart:
Item -> ShoppingCart
addToActive:
(ActiveCartData * Item) -> ShoppingCart
removeFromActive:
(ActiveCartData * Item) -> ShoppingCart
pay:
(ActiveCartData * Payment) -> ShoppingCart
might be empty or active
Modelling the shopping cart: Server API
let initCart item =
{ UnpaidItems = [item] }
let addToActive (cart : ActiveCart) item =
{ cart with UnpaidItems = item :: cart.existingItems }
create a new active cart
prepend item to list
Modelling the shopping cart: Client API
let addItem cart item =
match cart with
| EmptyCart ->
initCart item
| ActiveCart activeData ->
addToActive(activeData, item)
| PaidCart paidData ->
???
cannot accidentally alter a paid cart!
Modelling the shopping cart: Server API
let removeFromActive (cart : ActiveCart) item =
let remaining = removeFromList cart.existingItems item
match remaining with
| [] ->
EmptyCart
| _ ->
{ cart with UnpaidItems = remainingItems }
create a new ActiveCart with
the item remove
Modelling the shopping cart: Client API
let removeItem cart item =
match cart with
| EmptyCart->
???
| ActiveCart activeData ->
removeFromActive(activeData, item)
| PaidCart paidData ->
???
compiler won't let you remove from an empty cart!
Why design with state transitions?
- Each state can have different allowable data
- All states are explicitly documented
- All transitions are explicitly documented
- It's a design tool that forces you to think about every possible thing that can occur
Undelivered
Out for delivery
Delivered
Put on truck
Address not found
Signed for
How can you get your hands
on that awesome power?
options
- Change your language to either Scala, F#, OCaml or Haskell
- Do it in your current language but live with the pain
- Nulls are often everywhere in existing APIs
- Abundances of sum-like type hierarchies and Value-types in Java is very tiresome and verbose
- Use a subset of the features in your current language
Case study: Java8
Java 8 introduces java.util.Optional<T>
Includes a number of higher-order-functions to work with optional values: map, flatmap, filter, ifPresent, orElseGet
String version = "UNKNOWN"; if(computer != null){ Soundcard soundcard = computer.getSoundcard(); if(soundcard != null){ USB usb = soundcard.getUSB(); if(usb != null){ version = usb.getVersion(); } } }
unsafe!
String version = computer.getSoundcard().getUSB().getVersion();
public class Computer { private Optional<Soundcard> soundcard; public Optional<Soundcard> getSoundcard() { ... } ... } public class Soundcard { private Optional<USB> usb; public Optional<USB> getUSB() { ... } } public class USB{ public String getVersion(){ ... } }
String soundcardUsbName = computer.flatMap(Computer::getSoundcard) .flatMap(Soundcard::getUSB) .map(USB::getVersion) .orElse("UNKNOWN");
Cool links pointing to hot stuff
- Domain Driven Design with the F# type System [slides & video]
- Monadic Java [slides]
- Railway oriented programming [slides & video]
- Java 8 in Action [book]
- Functors, Applicatives, And Monads In Pictures [blog]
- Functors, Applicative Functors and Monoids [chapter]
- A Fistful of Monads [chapter]
- For a Few Monads More [chapter]
- Learn You a Haskell for Great Good! [book]
DDDing with Types
By Simon Stender Boisen
DDDing with Types
- 949