6 people like it.

Simple CQRS Inventory

a simple CQRS example, inspired by the blogpost http://tojans.me/blog/2014/02/26/cqrs-and-functional-programming/

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
type Id = string
type Name = string
type Amount = int
type Command = 
    | CreateInventoryItem of Id
    | RenameInventoryItem of Id * Name
    | RemoveItemsFromInventory of Id * Amount
    | AddItemsToInventory of Id * Amount
    | DeactivateInventoryItem of Id
type Event  = 
    | InventoryItemCreated of Id
    | InventoryItemRenamed of Id * Name
    | ItemsRemovedFromInventory of Id * Amount
    | ItemsCheckedInToInventory of Id * Amount
    | InventoryItemDeactivated of Id
type Item = { itemId : Id; activated : bool }

// NOTE: use Chessie or F# 4.1 instead
type Result<'TSuccess,'TFailure> = 
    | Ok of 'TSuccess
    | Error of 'TFailure
let handle command someItem = 
    match someItem, command with
    | None,      CreateInventoryItem ID -> Ok [ InventoryItemCreated ID ]
    | None, _ -> Error "please create the item first"
    | Some item, CreateInventoryItem ID -> Error "item already created"
    | Some item, DeactivateInventoryItem ID -> 
        if item.activated then Ok [InventoryItemDeactivated ID] else Error "already deactivated"
    | Some item, _ when not item.activated -> Error "item is deactivated"
    | Some item, RenameInventoryItem (ID, name) -> 
        if System.String.IsNullOrEmpty name then Error "invalid name"
        else Ok [InventoryItemRenamed (ID, name) ]
    | Some item, RemoveItemsFromInventory(ID, amount) ->
        if amount <= 0 then Error "Can't remove negative amoutn from inventory"
        else Ok [ItemsRemovedFromInventory (ID, amount)]
    | Some item, AddItemsToInventory(ID, amount) ->
        if amount <= 0 then Error "must have an amount > 0 to checkin to inventory"
        else Ok [ItemsCheckedInToInventory(ID,amount)]

let applyEvent someItem event =
    match someItem, event with
    | None, InventoryItemCreated ID -> Some { itemId = ID; activated = true}
    | Some item, InventoryItemDeactivated ID -> Some {item with activated = false }
    | Some item, _ -> Some item
    | None, _ -> None
let applyEvents = List.fold applyEvent
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type Name = string

Full name: Script.Name
type Amount = int

Full name: Script.Amount
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type Command =
  | CreateInventoryItem of Id
  | RenameInventoryItem of Id * Name
  | RemoveItemsFromInventory of Id * Amount
  | AddItemsToInventory of Id * Amount
  | DeactivateInventoryItem of Id

Full name: Script.Command
union case Command.CreateInventoryItem: Id -> Command
type Id = string

Full name: Script.Id
union case Command.RenameInventoryItem: Id * Name -> Command
union case Command.RemoveItemsFromInventory: Id * Amount -> Command
union case Command.AddItemsToInventory: Id * Amount -> Command
union case Command.DeactivateInventoryItem: Id -> Command
Multiple items
module Event

from Microsoft.FSharp.Control

--------------------
type Event =
  | InventoryItemCreated of Id
  | InventoryItemRenamed of Id * Name
  | ItemsRemovedFromInventory of Id * Amount
  | ItemsCheckedInToInventory of Id * Amount
  | InventoryItemDeactivated of Id

Full name: Script.Event

--------------------
type Event<'T> =
  new : unit -> Event<'T>
  member Trigger : arg:'T -> unit
  member Publish : IEvent<'T>

Full name: Microsoft.FSharp.Control.Event<_>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
  new : unit -> Event<'Delegate,'Args>
  member Trigger : sender:obj * args:'Args -> unit
  member Publish : IEvent<'Delegate,'Args>

Full name: Microsoft.FSharp.Control.Event<_,_>

--------------------
new : unit -> Event<'T>

--------------------
new : unit -> Event<'Delegate,'Args>
union case Event.InventoryItemCreated: Id -> Event
union case Event.InventoryItemRenamed: Id * Name -> Event
union case Event.ItemsRemovedFromInventory: Id * Amount -> Event
union case Event.ItemsCheckedInToInventory: Id * Amount -> Event
union case Event.InventoryItemDeactivated: Id -> Event
type Item =
  {itemId: Id;
   activated: bool;}

Full name: Script.Item
Item.itemId: Id
Item.activated: bool
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
type Result<'TSuccess,'TFailure> =
  | Ok of 'TSuccess
  | Error of 'TFailure

Full name: Script.Result<_,_>
union case Result.Ok: 'TSuccess -> Result<'TSuccess,'TFailure>
union case Result.Error: 'TFailure -> Result<'TSuccess,'TFailure>
val handle : command:Command -> someItem:Item option -> Result<Event list,string>

Full name: Script.handle
val command : Command
val someItem : Item option
union case Option.None: Option<'T>
val ID : Id
union case Option.Some: Value: 'T -> Option<'T>
val item : Item
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val name : Name
namespace System
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
System.String(value: nativeptr<char>) : unit
System.String(value: nativeptr<sbyte>) : unit
System.String(value: char []) : unit
System.String(c: char, count: int) : unit
System.String(value: nativeptr<char>, startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
System.String(value: char [], startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: System.Text.Encoding) : unit
System.String.IsNullOrEmpty(value: string) : bool
val amount : Amount
val applyEvent : someItem:Item option -> event:Event -> Item option

Full name: Script.applyEvent
val event : Event
val applyEvents : (Item option -> Event list -> Item option)

Full name: Script.applyEvents
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State

Full name: Microsoft.FSharp.Collections.List.fold
Raw view Test code New version

More information

Link:http://fssnip.net/7R8
Posted:7 years ago
Author:Andreas Vilinski
Tags: cqrs , domain modelling