Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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...
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 va...
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...
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...
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) -> ...
let square x = x * x
let add x y = x + y
let toString x = x.ToString()
let complexFunction x = toString (add 5 (square x))...
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...
let safeDiv x y =
match y with
| 0 -> None
| _ -> Some(x/y)
Pattern matching
let safeDiv x y =
match y with
| 0 -> None
| ...
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" }
le...
OK! Let’s go…
public class Company
{
public String BusinessName { get; set; }
public String TaxCode { get; set; }
public String VatNumbe...
Value object
An immutable object, like money or a date
range, whose equality isn't based on identity
(in general equality ...
Value object
An immutable object, like money or a date
range, whose equality isn't based on identity
(in general equality ...
public class CompanyProfile
{
public String BusinessName { get; private set; }
public String TaxCode { get; private set; }...
public class CompanyProfile
{
// same as before…
public override Boolean Equals(Object other)
{
var target = other as Comp...
type CompanyProfile = {
BusinessName : string;
TaxCode : string;
VatNumber : string
}
Value object in F#
option;
let profi...
Ubiquitous Language is the concept of
defining a language (spoken and written)
that is equally used across developers and
...
public class Company
{
...
public String AssignedBank { get; set; }
public Boolean IsBankAuthorized { get; set; }
}
Ubiqui...
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:...
Make illegal states unrepresentable
Ubiquitous language
type Bank = Bank of string
type UnauthorizedBank = UnauthorizedBank of Bank
type AuthorizedBank = AuthorizedBank of Bank
t...
type CompanyProfile = {
BusinessName: string,
TaxCode: string,
VatCode: string option
}
type Bank = Bank of string
type Un...
The central idea of specification is to
separate the statement of how to match
a candidate, from the candidate object
that...
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 (simpl...
let isOrderFullFilled : Spec<Order> =
let p order =
match Seq.isEmpty order.Items with
| true -> Failure("Items' list empt...
let And (left: Spec<'a>) (right: Spec<'a>) : Spec<'a> =
let p entity =
match left entity with
| Success -> right entity
| ...
let order = {
Number = "1234";
Customer = { Name = "Alessandro"; Category = Gold};
Items = [
{ Code = "Code1"; Quantity = ...
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 Sourc...
public void AddItemToCart(Item item)
{
// validation
if (item == null)
throw new ArgumentNullException();
// execution
_it...
public void AddItemToCart(Item item)
{
if (item == null)
throw new ArgumentNullException();
var domainEvent = new ItemAdde...
public void AddItemToCart(Item item)
{
if (item == null)
throw new ArgumentNullException();
var domainEvent = new ItemAdde...
public static Cart Apply(Cart target, CartCreated domainEvent)
{
return new Cart { Id = domainEvent.CartId, _items = new S...
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, ev...
Cart.Apply(
Cart.Apply(
Cart.Apply(null, new CartCreated { CartId=1}),
new ItemAddedToCart { CartId = 1, ItemId = "A"
}
),...
Cart.Apply(
Cart.Apply(
Cart.Apply(
Cart.Apply(null, new CartCreated { CartId=1}),
new ItemAddedToCart { CartId = 1, ItemI...
Executing a command:
type Exec =
( CartState * Command ) -> DomainEvent
Applying an event:
type Apply =
( CartState * Doma...
type Command =
| Create of string
| AddItem of int
| RemoveItem of int
| RemoveAllItems
| Checkout
type Event =
| Created ...
type CartState = {
Name: string;
Items: List<int>;
Active: bool;
}
let apply state = function
| Created x -> { Cart.empty ...
let domainEvents = [
Created("cart1");
ItemAdded(1);
ItemAdded(2);
Removed;
ItemAdded(3);
Checkedout;
]
let state = List.f...
Tackling Complexity in the
Heart of Software
Eric Evans
…but, when DDD?
Functional DDD
Functional DDD
Upcoming SlideShare
Loading in …5
×

Functional DDD

2,615 views

Published on

How functional languages, like F#, fit in domain driven design's world

  • Be the first to comment

Functional DDD

  1. 1. Functional Domain Driven Design disclaimer: is still a “work in progress”
  2. 2. Focus on the domain and domain logic rather than technology (Eric Evans)
  3. 3. Why not with a functional approach?
  4. 4. 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?
  5. 5. software developer @ codiceplastico @amelchiori alessandro@codiceplastico.com About me
  6. 6. F#: a gentle introduction
  7. 7. let myString = "original value" let myString = "new value” Immutability: values, not variables! Duplicate definition of value ‘myString’
  8. 8. let add x y = x + y add 2 2 x:int -> y:int -> int Type inference
  9. 9. void ProcessItems(Item[] items, Action<Item> action) { for(int i = 0; i < items.Length; i++) { var item = items[i]; action(item); } } Recursion
  10. 10. let rec processItems action = function | [] -> () | head :: tail -> action head; processItems tail Recursion
  11. 11. 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)
  12. 12. 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
  13. 13. 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
  14. 14. 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
  15. 15. 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)
  16. 16. type State = | On | Off let x = On let y = Off Discriminated unions
  17. 17. let div (x, y) = … (int * int) - > int Tuples let div (x, y) = match (x, y) with | (_, 0) -> None | (_, _) -> Some(x/ y)
  18. 18. type site = { Title : string; Url : string } Records let homepage = { Title = "Google"; Url = "http://www.google.com" } let next = { homepage with Title = "NextPage" }
  19. 19. OK! Let’s go…
  20. 20. 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; } }
  21. 21. 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
  22. 22. 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
  23. 23. 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
  24. 24. 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
  25. 25. type CompanyProfile = { BusinessName : string; TaxCode : string; VatNumber : string } Value object in F# option; let profile = { BusinessName = “CodicePlastico”; TaxNumber = “1234567890”; }
  26. 26. Ubiquitous Language is the concept of defining a language (spoken and written) that is equally used across developers and domain experts Ubiquitous language
  27. 27. public class Company { ... public String AssignedBank { get; set; } public Boolean IsBankAuthorized { get; set; } } Ubiquitous language
  28. 28. 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
  29. 29. Make illegal states unrepresentable Ubiquitous language
  30. 30. 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#
  31. 31. 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
  32. 32. 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
  33. 33. type SpecificationResult = | Success | Failure of string type Spec<'a> = 'a -> SpecificationResult Specification
  34. 34. 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; }
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. CQRS & Event Sourcing
  39. 39. A single model cannot be appropriate for reporting, searching and transactional behavior Greg Young CQRS
  40. 40. State transition are an important part of our problem space and should be modeled within our domain Greg Young Event Sourcing
  41. 41. public void AddItemToCart(Item item) { // validation if (item == null) throw new ArgumentNullException(); // execution _items.Add(item.Id); } Commands, events and…fold (step 1)
  42. 42. 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)
  43. 43. 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)
  44. 44. 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)
  45. 45. Cart.Apply(null, new CartCreated { CartId=1 }) Commands, events and…fold (step 5)
  46. 46. Cart.Apply( Cart.Apply(null, new CartCreated { CartId=1}), new ItemAddedToCart { CartId = 1, ItemId = "A" } ) Commands, events and…fold (step 5)
  47. 47. 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)
  48. 48. 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)
  49. 49. Executing a command: type Exec = ( CartState * Command ) -> DomainEvent Applying an event: type Apply = ( CartState * DomainEvent ) -> CartState Commands, events and…fold (step 6)
  50. 50. 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)
  51. 51. 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)
  52. 52. 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)
  53. 53. Tackling Complexity in the Heart of Software Eric Evans …but, when DDD?

×