Skip to content

Instantly share code, notes, and snippets.

@perokvist perokvist/RPS_2.md Secret
Created Feb 18, 2014

Embed
What would you like to do?

###Introduction In the last post we created a simple rock-paper-scissor game. There are still alot of improvments to be made to the Game it self, we're going to concentate on doing some simple command handling in this post. We're going to learn and use pipes "|>".

###Handling In order to handle a command, we need to;

  • Read all earlier events related to the game for the command.
  • Replay all these events on game
  • Apply the command on the game
  • Save the events caused by the command

To do this we need a store for the events, an eventstore. In our code we're going to fake the event store's load and save methods, to later revisit and implement/use something real.

###Piping This flow could represented in a readable way using "|>" pipe. This takes the result from the left add adds i as a paramter to the right. Our handle flow could be a function (called persist);

let persist store id f =
load store id |> rehydrate |> f |> save store id

Here we load, rehydrate and the handes the command via a function that we pass (more on that later) and finally saves. Notice that we pass around our store to load and save functions.

###Load To read all earlier events we create a load funcation, that takes a store.

let load store id

Our store is implemented as a dictionary to keep it simple. So the load function looks like;

let load store id = 
store.[id]

###Replay In the last post we created a function to restore the game state, a function called restoreState, that takes an initial state, events and return the current state.

let rehydrate events =
restoreState {creatorName="";creatorMove=Rock;gameState=NotStarted } events

In our handler we use this a function called rehydrate.

###Apply

Our persist function takes another function for handling the command, in the above code called f. It needs to return events to pass to the save function.

The store is a dictionary.

let eventStore = [("a",[{GameCreatedEvent.playerName="per";name="stanley cup"} :> Event;{MoveMadeEvent.playerName="per";MoveMadeEvent.move=Move.Rock} :> Event])] |> Map.ofList

With the store in place we create a function to help us using the store and persistance. A function called apply;

let apply = persist eventStore

Now we need something that takes commands and uses the apply function, and specifies how to command should be handled.

let handle (command:obj) = 
match command with
| :? MakeMoveCommand as c -> apply c.id (makeMove c)
| :? CreateGameCommand as c -> apply c.id (createGame c) 
| _ -> () 

Here a lot of interesting stuff going on. A function called handle takes any object as paramter. We then use our old friend match now togheter with ":?" to check the type of the command. If the command matches we create an alieas and use the apply function. The apply function uses our flow (persist function) passed the id and a function telling it how the command should be used on the rehydrated game. MakeMoveCommand is going to invoke makeMove on the Game that rehydrate returns with the command (c). Also note that the apply function is using persist and passing c.id and a function to handle the command.

###Save

Due to the simple nature of this implementation we let the save function be empty. Load and save get more interesting the day we chose to use a real eventstore. We could add the events to the dictonary.

let save store commandId events =
()

End

Let se how it all look put together. (Note the we changed the id's from guid to strings since last post).

open Microsoft.FSharp.Collections
open Game

let eventStore = [("a",[{GameCreatedEvent.playerName="per";name="stanley cup"} :> Event;{MoveMadeEvent.playerName="per";MoveMadeEvent.move=Move.Rock} :> Event])] |> Map.ofList

let load store id = 
    eventStore.[id]

let save store commandId events =
    ()
    
let rehydrate events =
    restoreState {creatorName="";creatorMove=Rock;gameState=NotStarted } events

let persist store id f =
    load store id |> rehydrate |> f |> save store id

let apply = persist eventStore

let handle (command:obj) = 
    match command with
    | :? MakeMoveCommand as c -> apply c.id (makeMove c)
    | :? CreateGameCommand as c -> apply c.id (createGame c) 
    | _ -> ()

Lessons learned

  • We learned how to use "|>" pipe to make a readable flow.
  • We learned how to use ":?" to match with types.
  • We made the apply to ease the use of the persist function, the apply function then takes the remaining parameters and passes the to persist.
  • We learned how to pass a function (persist parameter f)
  • We learned how pass the function and execute commands against Game (in the handle method)

###Conclusion We continued with creating the handler instead of improving the Game for the last post. The goal is to get working parts before revisiting them to learn more. As one reader pointed out on the comments of the last post, commands and events could be made as a discriminated union. Maybe something for future refactorings.

Enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.