Advertisement
Advertisement

More Related Content

Advertisement
Advertisement

Functional DDD

  1. Functional Domain Driven Design disclaimer: is still a “work in progress”
  2. Focus on the domain and domain logic rather than technology (Eric Evans)
  3. Why not with a functional approach?
  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. software developer @ codiceplastico @amelchiori alessandro@codiceplastico.com About me
  6. F#: a gentle introduction
  7. let myString = "original value" let myString = "new value” Immutability: values, not variables! Duplicate definition of value ‘myString’
  8. let add x y = x + y add 2 2 x:int -> y:int -> int Type inference
  9. void ProcessItems(Item[] items, Action<Item> action) { for(int i = 0; i < items.Length; i++) { var item = items[i]; action(item); } } Recursion
  10. let rec processItems action = function | [] -> () | head :: tail -> action head; processItems tail Recursion
  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. 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. 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. 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. 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. type State = | On | Off let x = On let y = Off Discriminated unions
  17. let div (x, y) = … (int * int) - > int Tuples let div (x, y) = match (x, y) with | (_, 0) -> None | (_, _) -> Some(x/ y)
  18. type site = { Title : string; Url : string } Records let homepage = { Title = "Google"; Url = "http://www.google.com" } let next = { homepage with Title = "NextPage" }
  19. OK! Let’s go…
  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. 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. 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. 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. 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. type CompanyProfile = { BusinessName : string; TaxCode : string; VatNumber : string } Value object in F# option; let profile = { BusinessName = “CodicePlastico”; TaxNumber = “1234567890”; }
  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. public class Company { ... public String AssignedBank { get; set; } public Boolean IsBankAuthorized { get; set; } } Ubiquitous language
  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. Make illegal states unrepresentable Ubiquitous language
  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. 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. 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. type SpecificationResult = | Success | Failure of string type Spec<'a> = 'a -> SpecificationResult Specification
  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. 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. 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. 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. CQRS & Event Sourcing
  39. A single model cannot be appropriate for reporting, searching and transactional behavior Greg Young CQRS
  40. State transition are an important part of our problem space and should be modeled within our domain Greg Young Event Sourcing
  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. 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. 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. 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. Cart.Apply(null, new CartCreated { CartId=1 }) Commands, events and…fold (step 5)
  46. Cart.Apply( Cart.Apply(null, new CartCreated { CartId=1}), new ItemAddedToCart { CartId = 1, ItemId = "A" } ) Commands, events and…fold (step 5)
  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. 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. Executing a command: type Exec = ( CartState * Command ) -> DomainEvent Applying an event: type Apply = ( CartState * DomainEvent ) -> CartState Commands, events and…fold (step 6)
  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. 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. 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. Tackling Complexity in the Heart of Software Eric Evans …but, when DDD?
Advertisement