Functional
Domain Driven Design
disclaimer: is still a “work in progress”
Focus on the domain and
domain logic rather than
technology
(Eric Evans)
Why not with a functional approach?
A monad is a triple (T, η, μ) where T is an
endofunctor T: X->X and η: I->T and μ: T x T-
>T are 2 natural transformations satisfying these
laws:
• Identity law: μ(η(T)) = T = μ(T(η))
• Associative law: μ(μ(T × T) × T)) = μ(T ×
μ(T × T))
A monad in X is just a monoid in the
category of endofunctors of X, with product
× replaced by composition of endofunctors
and unit set by the identity endofunctor
What is a monad?
software developer @ codiceplastico
@amelchiori
alessandro@codiceplastico.com
About me
F#: a gentle introduction
let myString = "original value"
let myString = "new value”
Immutability: values, not variables!
Duplicate definition of value ‘myString’
let add x y = x + y
add 2 2
x:int -> y:int -> int
Type inference
void ProcessItems(Item[] items, Action<Item> action)
{
for(int i = 0; i < items.Length; i++)
{
var item = items[i];
action(item);
}
}
Recursion
let rec processItems action = function
| [] -> ()
| head :: tail ->
action head;
processItems tail
Recursion
let add x y = x + y
x:int -> y:int -> int
Partial function application
let add5 x = add 5
x:int -> int
let value = add5 10
(value = 15)
let f x = x * x
x:int -> int
let g x = -x/2 + 5
x:int -> int
Functions composition
let (<<) f g x = f (g x)
('b -> 'c) -> ('a -> 'b) -> 'a -> ’c
let h x = f << g
x:int -> int
let square x = x * x
let add x y = x + y
let toString x = x.ToString()
let complexFunction x = toString (add 5 (square x))
Pipeline operator
let (|>) x f = f x
let complexFunction x = x |> square |> add 5 |> toString
let div x y = x / y
x:int -> y:int -> int
div 10 5 // 2
div 10 0 // System.DivideByZeroException
Option type
let safeDiv x y =
if y = 0 then None
else Some(x/y)
x:int -> y:int -> int option
let safeDiv x y =
match y with
| 0 -> None
| _ -> Some(x/y)
Pattern matching
let safeDiv x y =
match y with
| 0 -> None
| 1 -> Some(x/y)
Incomplete pattern matches on this expression.
For example, the value '2' may indicate a case not covered
by the pattern(s)
type State =
| On
| Off
let x = On
let y = Off
Discriminated unions
let div (x, y) = …
(int * int) - > int
Tuples
let div (x, y) =
match (x, y) with
| (_, 0) -> None
| (_, _) -> Some(x/ y)
type site = { Title : string; Url : string }
Records
let homepage =
{ Title = "Google"; Url = "http://www.google.com" }
let next = { homepage with Title = "NextPage" }
OK! Let’s go…
public class Company
{
public String BusinessName { get; set; }
public String TaxCode { get; set; }
public String VatNumber { get; set; }
public String AssignedBank { get; set; }
public Boolean IsBankAuthorized { get; set; }
}
Value object
An immutable object, like money or a date
range, whose equality isn't based on identity
(in general equality is based on all fields
equality)
Martin Fowler
Value object
An immutable object, like money or a date
range, whose equality isn't based on identity
(in general equality is based on all fields
equality)
Martin Fowler
public class CompanyProfile
{
public String BusinessName { get; private set; }
public String TaxCode { get; private set; }
public String VatNumber { get; private set; }
public CompanyProfile(String businessName, String taxCode,
String vatNumber=null)
{
// check if parameters are valid
BusinessName = businessName;
TaxCode = taxCode;
VatNumber = vatNumber;
}
}
Value object
public class CompanyProfile
{
// same as before…
public override Boolean Equals(Object other)
{
var target = other as CompanyProfile;
return target == null ? false :
target.BusinessName == this.BusinessName
&& target.TaxCode == this.TaxCode
&& target.VatCode == this.VatCode;
}
public override Int32 GetHashCode()
{
// ...
}
}
Value object’s equality
type CompanyProfile = {
BusinessName : string;
TaxCode : string;
VatNumber : string
}
Value object in F#
option;
let profile = {
BusinessName = “CodicePlastico”;
TaxNumber = “1234567890”;
}
Ubiquitous Language is the concept of
defining a language (spoken and written)
that is equally used across developers and
domain experts
Ubiquitous language
public class Company
{
...
public String AssignedBank { get; set; }
public Boolean IsBankAuthorized { get; set; }
}
Ubiquitous language
Rule 1:
A company must have a bank to work with
Rule 2:
A company can be authorized to work with its
assigned bank
Rule 3:
A company can be not authorized to work with
its assigned bank
Business logic
Make illegal states unrepresentable
Ubiquitous language
type Bank = Bank of string
type UnauthorizedBank = UnauthorizedBank of Bank
type AuthorizedBank = AuthorizedBank of Bank
type AssignedBank =
| Unauthorized of UnauthorizedBank
| Authorized of AuthorizedBank
Business logic in F#
type CompanyProfile = {
BusinessName: string,
TaxCode: string,
VatCode: string option
}
type Bank = Bank of string
type UnauthorizedBank =
UnauthorizedBank of Bank
type AuthorizedBank =
AuthorizedBank of Bank
type AssignedBank =
| Unauthorized of UnauthorizedBank
| Authorized of AuthorizedBank
type Company = {
Profile: CompanyProfile,
Bank: AssignedBank
}
Ubiquitous language to the rescue
The central idea of specification is to
separate the statement of how to match
a candidate, from the candidate object
that it is matched against
Specification
type SpecificationResult =
| Success
| Failure of string
type Spec<'a> = 'a -> SpecificationResult
Specification
type Category =
| Default
| Premium
| Gold
type Customer = {
Name: string;
Category: Category;
}
Specification: our (simple) domain
type Item = {
Code: string;
Quantity: int;
}
type Order = {
Number: string;
Customer: Customer;
Items: Item list;
}
let isOrderFullFilled : Spec<Order> =
let p order =
match Seq.isEmpty order.Items with
| true -> Failure("Items' list empty")
| false -> Success
in p
let isGoldCustomer : Spec<Customer> =
let p customer =
match customer.Category with
| Gold -> Success
| _ -> Failure("No-gold customer")
in p
Specification
let And (left: Spec<'a>) (right: Spec<'a>) : Spec<'a> =
let p entity =
match left entity with
| Success -> right entity
| _ -> Failure("And specification is not satisfied")
in p
Specification
let order = {
Number = "1234";
Customer = { Name = "Alessandro"; Category = Gold};
Items = [
{ Code = "Code1"; Quantity = 1};
{ Code = "Code2"; Quantity = 2};
];
}
let isOrderValid = And
isOrderFullFilled
(adapt isGoldCustomer (fun order -> order.Customer))
let isValid = isOrderValid order
Specification
CQRS & Event Sourcing
A single model cannot be appropriate
for reporting, searching and
transactional behavior
Greg Young
CQRS
State transition are an important part of our
problem space and should be modeled
within our domain
Greg Young
Event Sourcing
public void AddItemToCart(Item item)
{
// validation
if (item == null)
throw new ArgumentNullException();
// execution
_items.Add(item.Id);
}
Commands, events and…fold (step 1)
public void AddItemToCart(Item item)
{
if (item == null)
throw new ArgumentNullException();
var domainEvent = new ItemAddedToCart
{ CartId = this.Id, ItemId = item.Id };
Apply(domainEvent)
}
private void Apply(ItemAddedToCart domainEvent)
{
_items.Add(domainEvent.ItemId);
}
Commands, events and…fold (step 2)
public void AddItemToCart(Item item)
{
if (item == null)
throw new ArgumentNullException();
var domainEvent = new ItemAddedToCart
{ CartId = this.Id, ItemId = item.Id };
Apply(this, domainEvent)
}
private void Apply(Cart target, ItemAddedToCart domainEvent)
{
target._items.Add(domainEvent.ItemId);
}
Commands, events and…fold (step 3)
public static Cart Apply(Cart target, CartCreated domainEvent)
{
return new Cart { Id = domainEvent.CartId, _items = new String[0] };
}
public static Cart Apply(Cart target, ItemAddedToCart domainEvent)
{
var items = target._items.ToList();
items.Add(domainEvent.ItemId);
return new Cart { Id = domainEvent.CartId, _items = items };
}
public static Cart Apply(Cart target, ItemRemovedFromCart domainEvent)
{
var items = target._items.ToList();
items.Remove(domainEvent.ItemId);
return new Cart { Id = domainEvent.CartId, _items = items };
}
Commands, events and…fold (step 4)
Cart.Apply(null, new CartCreated { CartId=1 })
Commands, events and…fold (step 5)
Cart.Apply(
Cart.Apply(null, new CartCreated { CartId=1}),
new ItemAddedToCart { CartId = 1, ItemId = "A"
}
)
Commands, events and…fold (step 5)
Cart.Apply(
Cart.Apply(
Cart.Apply(null, new CartCreated { CartId=1}),
new ItemAddedToCart { CartId = 1, ItemId = "A"
}
),
new ItemAddedToCart { CartId = 1, ItemId = "B" }
)
Commands, events and…fold (step 5)
Cart.Apply(
Cart.Apply(
Cart.Apply(
Cart.Apply(null, new CartCreated { CartId=1}),
new ItemAddedToCart { CartId = 1, ItemId = "A"
}
),
new ItemAddedToCart { CartId = 1, ItemId = "B" }
),
new ItemRemovedFromCart { CartId = 1, ItemId = "A" }
)
Commands, events and…fold (step 5)
Executing a command:
type Exec =
( CartState * Command ) -> DomainEvent
Applying an event:
type Apply =
( CartState * DomainEvent ) -> CartState
Commands, events and…fold (step 6)
type Command =
| Create of string
| AddItem of int
| RemoveItem of int
| RemoveAllItems
| Checkout
type Event =
| Created of string
| ItemAdded of int
| ItemRemoved of
int
| AllItemsRemoved
| Checkedout
Commands, events and…fold (step 7)
type CartState = {
Name: string;
Items: List<int>;
Active: bool;
}
let apply state = function
| Created x -> { Cart.empty with Name = x }
| ItemAdded x -> { state with Items = List.append state.Items [x] }
| ItemRemoved x ->
{ state with Items = List.filter (fun i -> i <> x ) state.Items }
| Removed _ -> { state with Items = List.empty }
| Checkedout _ -> { state with Active = false }
Commands, events and…fold (step 8)
let domainEvents = [
Created("cart1");
ItemAdded(1);
ItemAdded(2);
Removed;
ItemAdded(3);
Checkedout;
]
let state = List.fold apply Cart.empty domainEvents
Commands, events and…fold (step 9)
Tackling Complexity in the
Heart of Software
Eric Evans
…but, when DDD?
Functional DDD

Functional DDD

  • 1.
    Functional Domain Driven Design disclaimer:is still a “work in progress”
  • 2.
    Focus on thedomain and domain logic rather than technology (Eric Evans)
  • 3.
    Why not witha functional approach?
  • 4.
    A monad isa triple (T, η, μ) where T is an endofunctor T: X->X and η: I->T and μ: T x T- >T are 2 natural transformations satisfying these laws: • Identity law: μ(η(T)) = T = μ(T(η)) • Associative law: μ(μ(T × T) × T)) = μ(T × μ(T × T)) A monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor What is a monad?
  • 6.
    software developer @codiceplastico @amelchiori alessandro@codiceplastico.com About me
  • 7.
    F#: a gentleintroduction
  • 8.
    let myString ="original value" let myString = "new value” Immutability: values, not variables! Duplicate definition of value ‘myString’
  • 9.
    let add xy = x + y add 2 2 x:int -> y:int -> int Type inference
  • 10.
    void ProcessItems(Item[] items,Action<Item> action) { for(int i = 0; i < items.Length; i++) { var item = items[i]; action(item); } } Recursion
  • 11.
    let rec processItemsaction = function | [] -> () | head :: tail -> action head; processItems tail Recursion
  • 12.
    let add xy = x + y x:int -> y:int -> int Partial function application let add5 x = add 5 x:int -> int let value = add5 10 (value = 15)
  • 13.
    let f x= x * x x:int -> int let g x = -x/2 + 5 x:int -> int Functions composition let (<<) f g x = f (g x) ('b -> 'c) -> ('a -> 'b) -> 'a -> ’c let h x = f << g x:int -> int
  • 14.
    let square x= x * x let add x y = x + y let toString x = x.ToString() let complexFunction x = toString (add 5 (square x)) Pipeline operator let (|>) x f = f x let complexFunction x = x |> square |> add 5 |> toString
  • 15.
    let div xy = x / y x:int -> y:int -> int div 10 5 // 2 div 10 0 // System.DivideByZeroException Option type let safeDiv x y = if y = 0 then None else Some(x/y) x:int -> y:int -> int option
  • 16.
    let safeDiv xy = match y with | 0 -> None | _ -> Some(x/y) Pattern matching let safeDiv x y = match y with | 0 -> None | 1 -> Some(x/y) Incomplete pattern matches on this expression. For example, the value '2' may indicate a case not covered by the pattern(s)
  • 17.
    type State = |On | Off let x = On let y = Off Discriminated unions
  • 18.
    let div (x,y) = … (int * int) - > int Tuples let div (x, y) = match (x, y) with | (_, 0) -> None | (_, _) -> Some(x/ y)
  • 19.
    type site ={ Title : string; Url : string } Records let homepage = { Title = "Google"; Url = "http://www.google.com" } let next = { homepage with Title = "NextPage" }
  • 20.
  • 21.
    public class Company { publicString BusinessName { get; set; } public String TaxCode { get; set; } public String VatNumber { get; set; } public String AssignedBank { get; set; } public Boolean IsBankAuthorized { get; set; } }
  • 22.
    Value object An immutableobject, like money or a date range, whose equality isn't based on identity (in general equality is based on all fields equality) Martin Fowler
  • 23.
    Value object An immutableobject, like money or a date range, whose equality isn't based on identity (in general equality is based on all fields equality) Martin Fowler
  • 24.
    public class CompanyProfile { publicString BusinessName { get; private set; } public String TaxCode { get; private set; } public String VatNumber { get; private set; } public CompanyProfile(String businessName, String taxCode, String vatNumber=null) { // check if parameters are valid BusinessName = businessName; TaxCode = taxCode; VatNumber = vatNumber; } } Value object
  • 25.
    public class CompanyProfile { //same as before… public override Boolean Equals(Object other) { var target = other as CompanyProfile; return target == null ? false : target.BusinessName == this.BusinessName && target.TaxCode == this.TaxCode && target.VatCode == this.VatCode; } public override Int32 GetHashCode() { // ... } } Value object’s equality
  • 26.
    type CompanyProfile ={ BusinessName : string; TaxCode : string; VatNumber : string } Value object in F# option; let profile = { BusinessName = “CodicePlastico”; TaxNumber = “1234567890”; }
  • 27.
    Ubiquitous Language isthe concept of defining a language (spoken and written) that is equally used across developers and domain experts Ubiquitous language
  • 28.
    public class Company { ... publicString AssignedBank { get; set; } public Boolean IsBankAuthorized { get; set; } } Ubiquitous language
  • 29.
    Rule 1: A companymust have a bank to work with Rule 2: A company can be authorized to work with its assigned bank Rule 3: A company can be not authorized to work with its assigned bank Business logic
  • 30.
    Make illegal statesunrepresentable Ubiquitous language
  • 31.
    type Bank =Bank of string type UnauthorizedBank = UnauthorizedBank of Bank type AuthorizedBank = AuthorizedBank of Bank type AssignedBank = | Unauthorized of UnauthorizedBank | Authorized of AuthorizedBank Business logic in F#
  • 32.
    type CompanyProfile ={ BusinessName: string, TaxCode: string, VatCode: string option } type Bank = Bank of string type UnauthorizedBank = UnauthorizedBank of Bank type AuthorizedBank = AuthorizedBank of Bank type AssignedBank = | Unauthorized of UnauthorizedBank | Authorized of AuthorizedBank type Company = { Profile: CompanyProfile, Bank: AssignedBank } Ubiquitous language to the rescue
  • 33.
    The central ideaof specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against Specification
  • 34.
    type SpecificationResult = |Success | Failure of string type Spec<'a> = 'a -> SpecificationResult Specification
  • 35.
    type Category = |Default | Premium | Gold type Customer = { Name: string; Category: Category; } Specification: our (simple) domain type Item = { Code: string; Quantity: int; } type Order = { Number: string; Customer: Customer; Items: Item list; }
  • 36.
    let isOrderFullFilled :Spec<Order> = let p order = match Seq.isEmpty order.Items with | true -> Failure("Items' list empty") | false -> Success in p let isGoldCustomer : Spec<Customer> = let p customer = match customer.Category with | Gold -> Success | _ -> Failure("No-gold customer") in p Specification
  • 37.
    let And (left:Spec<'a>) (right: Spec<'a>) : Spec<'a> = let p entity = match left entity with | Success -> right entity | _ -> Failure("And specification is not satisfied") in p Specification
  • 38.
    let order ={ Number = "1234"; Customer = { Name = "Alessandro"; Category = Gold}; Items = [ { Code = "Code1"; Quantity = 1}; { Code = "Code2"; Quantity = 2}; ]; } let isOrderValid = And isOrderFullFilled (adapt isGoldCustomer (fun order -> order.Customer)) let isValid = isOrderValid order Specification
  • 39.
    CQRS & EventSourcing
  • 40.
    A single modelcannot be appropriate for reporting, searching and transactional behavior Greg Young CQRS
  • 41.
    State transition arean important part of our problem space and should be modeled within our domain Greg Young Event Sourcing
  • 42.
    public void AddItemToCart(Itemitem) { // validation if (item == null) throw new ArgumentNullException(); // execution _items.Add(item.Id); } Commands, events and…fold (step 1)
  • 43.
    public void AddItemToCart(Itemitem) { if (item == null) throw new ArgumentNullException(); var domainEvent = new ItemAddedToCart { CartId = this.Id, ItemId = item.Id }; Apply(domainEvent) } private void Apply(ItemAddedToCart domainEvent) { _items.Add(domainEvent.ItemId); } Commands, events and…fold (step 2)
  • 44.
    public void AddItemToCart(Itemitem) { if (item == null) throw new ArgumentNullException(); var domainEvent = new ItemAddedToCart { CartId = this.Id, ItemId = item.Id }; Apply(this, domainEvent) } private void Apply(Cart target, ItemAddedToCart domainEvent) { target._items.Add(domainEvent.ItemId); } Commands, events and…fold (step 3)
  • 45.
    public static CartApply(Cart target, CartCreated domainEvent) { return new Cart { Id = domainEvent.CartId, _items = new String[0] }; } public static Cart Apply(Cart target, ItemAddedToCart domainEvent) { var items = target._items.ToList(); items.Add(domainEvent.ItemId); return new Cart { Id = domainEvent.CartId, _items = items }; } public static Cart Apply(Cart target, ItemRemovedFromCart domainEvent) { var items = target._items.ToList(); items.Remove(domainEvent.ItemId); return new Cart { Id = domainEvent.CartId, _items = items }; } Commands, events and…fold (step 4)
  • 46.
    Cart.Apply(null, new CartCreated{ CartId=1 }) Commands, events and…fold (step 5)
  • 47.
    Cart.Apply( Cart.Apply(null, new CartCreated{ CartId=1}), new ItemAddedToCart { CartId = 1, ItemId = "A" } ) Commands, events and…fold (step 5)
  • 48.
    Cart.Apply( Cart.Apply( Cart.Apply(null, new CartCreated{ CartId=1}), new ItemAddedToCart { CartId = 1, ItemId = "A" } ), new ItemAddedToCart { CartId = 1, ItemId = "B" } ) Commands, events and…fold (step 5)
  • 49.
    Cart.Apply( Cart.Apply( Cart.Apply( Cart.Apply(null, new CartCreated{ CartId=1}), new ItemAddedToCart { CartId = 1, ItemId = "A" } ), new ItemAddedToCart { CartId = 1, ItemId = "B" } ), new ItemRemovedFromCart { CartId = 1, ItemId = "A" } ) Commands, events and…fold (step 5)
  • 50.
    Executing a command: typeExec = ( CartState * Command ) -> DomainEvent Applying an event: type Apply = ( CartState * DomainEvent ) -> CartState Commands, events and…fold (step 6)
  • 51.
    type Command = |Create of string | AddItem of int | RemoveItem of int | RemoveAllItems | Checkout type Event = | Created of string | ItemAdded of int | ItemRemoved of int | AllItemsRemoved | Checkedout Commands, events and…fold (step 7)
  • 52.
    type CartState ={ Name: string; Items: List<int>; Active: bool; } let apply state = function | Created x -> { Cart.empty with Name = x } | ItemAdded x -> { state with Items = List.append state.Items [x] } | ItemRemoved x -> { state with Items = List.filter (fun i -> i <> x ) state.Items } | Removed _ -> { state with Items = List.empty } | Checkedout _ -> { state with Active = false } Commands, events and…fold (step 8)
  • 53.
    let domainEvents =[ Created("cart1"); ItemAdded(1); ItemAdded(2); Removed; ItemAdded(3); Checkedout; ] let state = List.fold apply Cart.empty domainEvents Commands, events and…fold (step 9)
  • 54.
    Tackling Complexity inthe Heart of Software Eric Evans …but, when DDD?