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.
EnterpriseTic-Tac-Toe
@ScottWlaschin
fsharpforfunandprofit.com
In which I ridiculously over-engineer a simple game to
crea...
EnterpriseTic-Tac-Toe
What you need for “Enterprise”?
• Siloed organization Specialized teams
• ArchitectureTeam
• Project ManagementTeam
• Fron...
What you need for “Enterprise”?
• Separation of concerns so that specialist teams can work
on different parts of the code ...
Project Manager: "We need
separation of concerns because
the front-end team and back-
end team hate each other and
refuse ...
Front-end team: "We need a
documented API so that
those dummies building
the back-end won't keep
breaking our code on
ever...
Back-end team: "We need a
security model because those
idiots building the front-end
will always find a way to do
somethin...
Maintenance team: "We
need well-documented
design because we're fed
up of having to reverse
engineer the hacked-up
spaghet...
Testers and Operations: "We
need auditing and logging so
that we can see what the effing
system is doing inside."
Auditing...
Everyone: "We don't really need scalability at all, but
the CTO wants to us to be buzzword compliant."
Scalability to ensu...
I’m gonna use F#, so here’s
all the F# you need
type Pair = int * int "*" means a pair
type Payment =
| Cash
| Card of CardNumber
"|" means a choice
typeThing = { name:st...
type AFunction =
string ->
int
A Function
42
“Deep
Thought”
A Function
type AFunction =
string ->
int * bool
Input is a string
Output is a pair
A Function
type AFunction =
bool * string ->
int
Input is a pair
Output is a int
TYPE DRIVEN DESIGN
Growing functional software,
guided by types
Type driven design
• Design with types only
– no implementation code.
• Every use-case/scenario corresponds to a
function ...
Tic-Tac-Toe Scenarios
• Initialize a game
• A move by Player X
• A move by Player O
type StartGame =
unit ->
GameState
StartGame
Game Statenothing
OtherStuff
Player X
Moves
type PlayerXMove =
GameState * SomeOtherStuff ->
GameState
GameState
(before) GameState
(after)
...
OtherStuff
Player O
Moves
type PlayerOMove =
GameState * SomeOtherStuff ->
GameState
GameState
(before) GameState
(after)
...
UserAction
Any
Kind of
Move
type UserAction =
| MoveLeft
| MoveRight
| Jump
| Fire
GameState
(before) GameState
(after)
Ge...
UserAction
Any
Kind of
Move
type UserAction =
| PlayerXMove of SomeStuff
| PlayerOMove of SomeStuff
GameState
(before) Gam...
type PlayerXMove =
GameState * PlayerX's Stuff ->
GameState
type PlayerOMove =
GameState * PlayerO's Stuff ->
GameState
Ea...
What is the other Stuff?
For some domains there might be a LOT of stuff...
But in Tic-Tac-Toe, it's just the location on t...
type PlayerXMove =
GameState * CellPosition ->
GameState
type PlayerOMove =
GameState * CellPosition ->
GameState
Same aga...
type PlayerXMove =
GameState * PlayerXPos ->
GameState
type PlayerOMove =
GameState * PlayerOPos ->
GameState
type PlayerX...
What is the GameState?
type GameState = { cells : Cell list }
type CellState =
| X
| O
| Empty
type Cell = {
pos : CellPos...
What is the GameState?
type GameState = { cells : Cell list }
type Player = PlayerX | PlayerO
type CellState =
| Played of...
What is the Output?
What does the UI need to know?
The UI should not have to "think" -- it should
just follow instructions.
What is the Output?
1) Pass the entire game state to the UI?
But the GameState should be opaque...
What is the Output?
1) Pass the entire game state to the UI?
2) Make the UI's life easier by explicitly returning
the cell...
What is the Output?
1) Pass the entire game state to the UI?
2) Make the UI's life easier by explicitly returning
the cell...
Time for a walkthrough...
Start game
Player X moves
Player O moves
Player X moves
Player O moves
Player X moves
Player X w...
Time for a walkthrough...
Start game
Player X moves
Player O moves
Player X moves
Player O moves
Player X moves
Player X w...
When does the game stop?
type GameStatus =
| InProcess
| Won of Player
| Tie
type PlayerXMove =
GameState * PlayerXPos ->
...
Review
What kind of errors can happen?
• Could the UI create an invalid GameState?
– No. We’re going to keep the internals of the...
Returning the available moves
type ValidMovesForPlayerX = PlayerXPos list
type ValidMovesForPlayerO = PlayerOPos list
type...
What kind of errors can happen?
• Could the UI pass in a valid CellPosition but at the wrong time?
– No, it is not in the ...
Some refactoring (before)
type GameStatus =
| InProcess
| Won of Player
| Tie
type PlayerXMove =
GameState * PlayerXPos ->...
Some refactoring (after)
type MoveResult =
| PlayerXToMove of GameState * ValidMovesForPlayerX
| PlayerOToMove of GameStat...
Time for a demo!
Hiding implementations with
Parametric Polymorphism
Hiding implementations with
Parametric Polymorphism
Enforcing encapsulation
• Decouple the "interface" from the
"implementation".
• Shared data structures that are used by bo...
Enforcing encapsulation
• OO approaches:
– Represent GameState with an abstract base class
– Represent GameState with an i...
Enforcing encapsulation
• FP approach:
– Make the UI use a generic GameState
– GameState can stay public
– All access to G...
With a generic GameState
type PlayerXMove<'GameState> =
'GameState * PlayerXPos ->
'GameState * MoveResult
type PlayerOMov...
Logging
move
We want to log the input
and output. But how?
Logging
move
Logging
log
Step 1: Create a log function
move
Logging
log
Step 2: glue all the functions
together using composition
log
move
Logging
log
Step 2: glue all the functions
together using composition
log
move
Logging
log
Step 3: use the new function in
place of old function
log
There's no need for a "decorator pattern"
in FP...
Demo of logging
Client-server communication
How do you send domain
objects on the wire?
JSON over HTTP?
Enterprise Rating: C-

What communication method should we use?
XML & SOAP?
Enterprise Rating: A

What communication method should we use?
Enterprise Service Bus!
Enterprise Rating: A++

What communication method should we use?
Sending objects on the wire
type MoveResultDTO = {
moveResultType : string // e.g. "PlayerXToMove"
gameStateToken : string...
Demo of problems
Stupid people Evil people
What’s the difference? 
POLA &
Capability Based Security
Evolution of a configuration API
Say that the UI needs to set a
configuration option
(e.g. DontShowThisMessageAgain)
How c...
Attempt 1
Give the caller the configuration file name
interface IConfiguration
{
string GetConfigFilename();
}
var filenam...
Attempt 2
Give the caller aTextWriter
interface IConfiguration
{
TextWriter GetConfigWriter();
}
var writer = config.GetCo...
Attempt 3
Give the caller a key/value interface
interface IConfiguration
{
void SetConfig(string key, string value);
}
con...
Attempt 4
Give the caller a domain-centric interface
enum MessageFlag {
ShowThisMessageAgain,
DontShowThisMessageAgain
}
i...
Attempt 5
Give the caller only the interface they need
interface IWarningMessageConfiguration
{
void SetMessageFlag(Messag...
Good security implies
good design
Good security is good design
• Filename => limit ourselves to file-based config files.
– ATextWriter makes the design is m...
Capability based design
• In a cap-based design, the caller can only do
exactly one thing -- a "capability".
• In this exa...
Attempt 5
A one method interface is a function
interface IWarningMessageConfiguration
{
void SetMessageFlag(MessageFlag va...
Capability Based Security and
Tic-Tac-Toe
Switching to cap-basedTic-Tac-Toe
type MoveResult =
| PlayerXToMove of
DisplayInfo * NextMoveInfo list
| PlayerOToMove of
...
Cap-based Demo
HATEOAS
Hypermedia As The Engine
Of Application State
“A REST client needs no prior knowledge
about how to interact with a...
How NOT to do HATEOAS
POST /customers/
GET /customer/42
If you know the API
you’re doing it wrong
How to do HATEOAS
POST /81f2300b618137d21d /
GET /da3f93e69b98
You can only know what URIs
to use by parsing the page
HATEOAS Demo
Some Benefits of HATEOAS
• The server owns the API model and can
change it without breaking any clients
– E.g. Change link...
Review: How “enterprise” are we?
• Separation of concerns
• A documented API
• Well-documented design
type MoveResult =
| ...
Review: How “enterprise” are we?
• A security model
• Auditing and logging
• Scalability
You can just waffle here:
"immuta...
Thanks!
@ScottWlaschin
fsharpforfunandprofit.com/ettt
fsharpworks.com
Contact me
Slides and video here
Let us know if you
...
Upcoming SlideShare
Loading in …5
×

Enterprise Tic-Tac-Toe

11,076 views

Published on

Video and related blog posts at fsharpforfunandprofit.com/ettt

Follow along as I ridiculously over-engineer a simple game to demonstrate how functional programming can be used to create a real-world "enterprise-ready" application.

Topics covered include: encoding business rules into types, data hiding with parametric polymorphism, using functions for capability-based security, exposing a REST API with HATEAOS, the functional approach to logging, actors, scalability and more.

Published in: Software

Enterprise Tic-Tac-Toe

  1. 1. EnterpriseTic-Tac-Toe @ScottWlaschin fsharpforfunandprofit.com In which I ridiculously over-engineer a simple game to create a real-world "enterprise-ready" application. Proper name is "Noughts and Crosses" btw
  2. 2. EnterpriseTic-Tac-Toe
  3. 3. What you need for “Enterprise”? • Siloed organization Specialized teams • ArchitectureTeam • Project ManagementTeam • Front End DevelopmentTeam • Back End DevelopmentTeam • OperationsTeam • SecurityTeam • Compliance and AuditingTeam Can we make them all happy?
  4. 4. What you need for “Enterprise”? • Separation of concerns so that specialist teams can work on different parts of the code at the same time. • A documented API so that the different teams can work effectively in parallel. • A security model to prevent unauthorized actions from occurring. • Well-documented design so that the architect can ensure that the implementation matches the UML diagrams. • Auditing and logging to ensure that the system is compliant. • Scalability to ensure that the system is ready for the challenges of rapid customer acquisition.
  5. 5. Project Manager: "We need separation of concerns because the front-end team and back- end team hate each other and refuse to work in the same room." Separation of concerns so that specialist teams can work on different parts of the code at the same time.
  6. 6. Front-end team: "We need a documented API so that those dummies building the back-end won't keep breaking our code on every commit." A documented API so that the different teams can work effectively in parallel.
  7. 7. Back-end team: "We need a security model because those idiots building the front-end will always find a way to do something stupid unless we constrain them." A security model to prevent unauthorized actions from occurring.
  8. 8. Maintenance team: "We need well-documented design because we're fed up of having to reverse engineer the hacked-up spaghetti being thrown at us." Well-documented design so that the architect can ensure that the implementation matches the UML diagrams.
  9. 9. Testers and Operations: "We need auditing and logging so that we can see what the effing system is doing inside." Auditing and logging to ensure that the system is compliant.
  10. 10. Everyone: "We don't really need scalability at all, but the CTO wants to us to be buzzword compliant." Scalability to ensure that the system is ready for the challenges of rapid customer acquisition.
  11. 11. I’m gonna use F#, so here’s all the F# you need
  12. 12. type Pair = int * int "*" means a pair type Payment = | Cash | Card of CardNumber "|" means a choice typeThing = { name:string } "{" means a record
  13. 13. type AFunction = string -> int A Function 42 “Deep Thought”
  14. 14. A Function type AFunction = string -> int * bool Input is a string Output is a pair
  15. 15. A Function type AFunction = bool * string -> int Input is a pair Output is a int
  16. 16. TYPE DRIVEN DESIGN
  17. 17. Growing functional software, guided by types
  18. 18. Type driven design • Design with types only – no implementation code. • Every use-case/scenario corresponds to a function type – one input and one output • Work mostly top-down and outside-in – Occasionally bottom up as well. • We ignore the UI for now.
  19. 19. Tic-Tac-Toe Scenarios • Initialize a game • A move by Player X • A move by Player O
  20. 20. type StartGame = unit -> GameState StartGame Game Statenothing
  21. 21. OtherStuff Player X Moves type PlayerXMove = GameState * SomeOtherStuff -> GameState GameState (before) GameState (after) Before After Loop
  22. 22. OtherStuff Player O Moves type PlayerOMove = GameState * SomeOtherStuff -> GameState GameState (before) GameState (after)  both functions look exactly the same and could be easily substituted for each other.
  23. 23. UserAction Any Kind of Move type UserAction = | MoveLeft | MoveRight | Jump | Fire GameState (before) GameState (after) Generic approach
  24. 24. UserAction Any Kind of Move type UserAction = | PlayerXMove of SomeStuff | PlayerOMove of SomeStuff GameState (before) GameState (after) Generic approach applied to this game But we have TWO players so should have two functions....
  25. 25. type PlayerXMove = GameState * PlayerX's Stuff -> GameState type PlayerOMove = GameState * PlayerO's Stuff -> GameState Each type is different and the compiler won’t let them be mixed up!
  26. 26. What is the other Stuff? For some domains there might be a LOT of stuff... But in Tic-Tac-Toe, it's just the location on the grid where the player makes their mark. type HorizPosition = Left | Middle | Right type VertPosition = Top | Center | Bottom type CellPosition = HorizPosition * VertPosition
  27. 27. type PlayerXMove = GameState * CellPosition -> GameState type PlayerOMove = GameState * CellPosition -> GameState Same again 
  28. 28. type PlayerXMove = GameState * PlayerXPos -> GameState type PlayerOMove = GameState * PlayerOPos -> GameState type PlayerXPos = PlayerXPos of CellPosition type PlayerOPos = PlayerOPos of CellPosition Different functions Different positions
  29. 29. What is the GameState? type GameState = { cells : Cell list } type CellState = | X | O | Empty type Cell = { pos : CellPosition state : CellState }
  30. 30. What is the GameState? type GameState = { cells : Cell list } type Player = PlayerX | PlayerO type CellState = | Played of Player | Empty type Cell = { pos : CellPosition state : CellState } Refactor!
  31. 31. What is the Output? What does the UI need to know? The UI should not have to "think" -- it should just follow instructions.
  32. 32. What is the Output? 1) Pass the entire game state to the UI? But the GameState should be opaque...
  33. 33. What is the Output? 1) Pass the entire game state to the UI? 2) Make the UI's life easier by explicitly returning the cells that changed with each move type PlayerXMove = GameState * PlayerXPos -> GameState * ChangedCells Too much trouble in this case
  34. 34. What is the Output? 1) Pass the entire game state to the UI? 2) Make the UI's life easier by explicitly returning the cells that changed with each move 3)The UI keeps track itself but can ask the server if it ever gets out of sync type GetCells = GameState -> Cell list
  35. 35. Time for a walkthrough... Start game Player X moves Player O moves Player X moves Player O moves Player X moves Player X wins!
  36. 36. Time for a walkthrough... Start game Player X moves Player O moves Player X moves Player O moves Player X moves Player X wins! Player O moves Player X moves Player O moves Player X moves Player O moves Player X moves Player O moves Player X moves Did I mention that the UI was stupid?
  37. 37. When does the game stop? type GameStatus = | InProcess | Won of Player | Tie type PlayerXMove = GameState * PlayerXPos -> GameState * GameStatus How does the UI know? Returned with the GameState
  38. 38. Review
  39. 39. What kind of errors can happen? • Could the UI create an invalid GameState? – No. We’re going to keep the internals of the game state hidden from the UI. • Could the UI pass in an invalid CellPosition? – No. The horizontal/vertical parts of CellPosition are restricted. • Could the UI pass in a valid CellPosition but at the wrong time? – Yes -- that is totally possible. • Could the UI allow player X to play twice in a row? – Again, yes. Nothing in our design prevents this. • What about when the game has ended but the stupid UI forgets to check the GameStatus and doesn't notice. – The game logic needs to not accept moves after the end!     
  40. 40. Returning the available moves type ValidMovesForPlayerX = PlayerXPos list type ValidMovesForPlayerO = PlayerOPos list type PlayerXMove = GameState * PlayerXPos -> GameState * GameStatus * ValidMovesForPlayerO type PlayerOMove = GameState * PlayerOPos -> GameState * GameStatus * ValidMovesForPlayerX Now returned after each move
  41. 41. What kind of errors can happen? • Could the UI pass in a valid CellPosition but at the wrong time? – No, it is not in the list of allowed moves. • Could the UI allow player X to play twice in a row? – No, the returned list only has moves for Player O • What about when the game has ended but the stupid UI forgets to check the GameStatus and doesn't notice. – The list of moves is empty when the game is over   type PlayerXMove = GameState * PlayerXPos -> GameState * GameStatus * ValidMovesForPlayerO type PlayerOMove = GameState * PlayerOPos -> GameState * GameStatus * ValidMovesForPlayerX 
  42. 42. Some refactoring (before) type GameStatus = | InProcess | Won of Player | Tie type PlayerXMove = GameState * PlayerXPos -> GameState * GameStatus * ValidMovesForPlayerO Merge into one type
  43. 43. Some refactoring (after) type MoveResult = | PlayerXToMove of GameState * ValidMovesForPlayerX | PlayerOToMove of GameState * ValidMovesForPlayerO | GameWon of GameState * Player | GameTied of GameState type PlayerXMove = GameState * PlayerXPos -> MoveResult type PlayerOMove = GameState * PlayerOPos -> MoveResult
  44. 44. Time for a demo!
  45. 45. Hiding implementations with Parametric Polymorphism
  46. 46. Hiding implementations with Parametric Polymorphism
  47. 47. Enforcing encapsulation • Decouple the "interface" from the "implementation". • Shared data structures that are used by both the UI and the game engine. (CellState, MoveResult, PlayerXPos, etc.) • Private data structures that should only be accessed by the game engine (e,g. GameState)
  48. 48. Enforcing encapsulation • OO approaches: – Represent GameState with an abstract base class – Represent GameState with an interface – Make constructor private
  49. 49. Enforcing encapsulation • FP approach: – Make the UI use a generic GameState – GameState can stay public – All access to GameState internals is via functions • These functions “injected” into the UI With List<T>, you can work with the list in many ways, but you cannot know what the T is, and you can never accidentally write code that assumes that T is an int or a string or a bool. This "hidden-ness" is not changed even when T is a public type.
  50. 50. With a generic GameState type PlayerXMove<'GameState> = 'GameState * PlayerXPos -> 'GameState * MoveResult type PlayerOMove<'GameState> = 'GameState * PlayerOPos -> 'GameState * MoveResult The UI is injected with these functions but doesn’t know what the GameState *really* is.
  51. 51. Logging
  52. 52. move We want to log the input and output. But how? Logging
  53. 53. move Logging log Step 1: Create a log function
  54. 54. move Logging log Step 2: glue all the functions together using composition log
  55. 55. move Logging log Step 2: glue all the functions together using composition log
  56. 56. move Logging log Step 3: use the new function in place of old function log There's no need for a "decorator pattern" in FP - it's just regular composition
  57. 57. Demo of logging
  58. 58. Client-server communication How do you send domain objects on the wire?
  59. 59. JSON over HTTP? Enterprise Rating: C-  What communication method should we use?
  60. 60. XML & SOAP? Enterprise Rating: A  What communication method should we use?
  61. 61. Enterprise Service Bus! Enterprise Rating: A++  What communication method should we use?
  62. 62. Sending objects on the wire type MoveResultDTO = { moveResultType : string // e.g. "PlayerXToMove" gameStateToken : string // only applicable in some cases availableMoves : int list } type MoveResult = | PlayerXToMove of GameState * ValidMovesForPlayerX | PlayerOToMove of GameState * ValidMovesForPlayerO | GameWon of GameState * Player | GameTied of GameState Not serialization friendly JSON/XML friendly
  63. 63. Demo of problems
  64. 64. Stupid people Evil people What’s the difference? 
  65. 65. POLA & Capability Based Security
  66. 66. Evolution of a configuration API Say that the UI needs to set a configuration option (e.g. DontShowThisMessageAgain) How can we stop a malicious caller doing bad things?
  67. 67. Attempt 1 Give the caller the configuration file name interface IConfiguration { string GetConfigFilename(); } var filename = config.GetConfigFilename(); // open file // write new config // close file API Caller  A malicious caller has the ability to write to any file on the filesystem
  68. 68. Attempt 2 Give the caller aTextWriter interface IConfiguration { TextWriter GetConfigWriter(); } var writer = config.GetConfigWriter(); // write new config API Caller  A malicious caller can corrupt the config file
  69. 69. Attempt 3 Give the caller a key/value interface interface IConfiguration { void SetConfig(string key, string value); } config.SetConfig( "DontShowThisMessageAgain", "True"); API Caller  A malicious caller can set the value to a non-boolean
  70. 70. Attempt 4 Give the caller a domain-centric interface enum MessageFlag { ShowThisMessageAgain, DontShowThisMessageAgain } interface IConfiguration { void SetMessageFlag(MessageFlag value); void SetConnectionString(ConnectionString value); void SetBackgroundColor(Color value); } API  What's to stop a malicious caller changing the connection string when they were only supposed to set the flag?
  71. 71. Attempt 5 Give the caller only the interface they need interface IWarningMessageConfiguration { void SetMessageFlag(MessageFlag value); } API  The caller can *only* do the thing we allow them to do.
  72. 72. Good security implies good design
  73. 73. Good security is good design • Filename => limit ourselves to file-based config files. – ATextWriter makes the design is more mockable • TextWriter => exposing a specific storage format – A generic KeyValue store make implementation choices more flexible. • KeyValue store using strings means possible bugs – Need to write validation and tests for that  – Statically typed interface means no corruption checking code.  • An interface with too many methods means no ISP – Reduce the number of available methods to one!
  74. 74. Capability based design • In a cap-based design, the caller can only do exactly one thing -- a "capability". • In this example, the caller has a capability to set the message flag, and that's all. Stops malicious AND stupid callers doing bad things!
  75. 75. Attempt 5 A one method interface is a function interface IWarningMessageConfiguration { void SetMessageFlag(MessageFlag value); } OO API Action<MessageFlag> messageFlagCapability Functional API
  76. 76. Capability Based Security and Tic-Tac-Toe
  77. 77. Switching to cap-basedTic-Tac-Toe type MoveResult = | PlayerXToMove of DisplayInfo * NextMoveInfo list | PlayerOToMove of DisplayInfo * NextMoveInfo list | GameWon of DisplayInfo * Player | GameTied of DisplayInfo type NextMoveInfo = { posToPlay : CellPosition capability : MoveCapability } This is a function This is for UI information only. The position is "baked" into the capability
  78. 78. Cap-based Demo
  79. 79. HATEOAS Hypermedia As The Engine Of Application State “A REST client needs no prior knowledge about how to interact with any particular application or server beyond a generic understanding of hypermedia.” RESTful done right
  80. 80. How NOT to do HATEOAS POST /customers/ GET /customer/42 If you know the API you’re doing it wrong
  81. 81. How to do HATEOAS POST /81f2300b618137d21d / GET /da3f93e69b98 You can only know what URIs to use by parsing the page
  82. 82. HATEOAS Demo
  83. 83. Some Benefits of HATEOAS • The server owns the API model and can change it without breaking any clients – E.g. Change links to point to CDN – E.g. Versioning • Simple client logic • Explorable API
  84. 84. Review: How “enterprise” are we? • Separation of concerns • A documented API • Well-documented design type MoveResult = | PlayerXToMove of GameState * ValidMovesForPlayerX | PlayerOToMove of GameState * ValidMovesForPlayerO | GameWon of GameState * Player | GameTied of GameState   
  85. 85. Review: How “enterprise” are we? • A security model • Auditing and logging • Scalability You can just waffle here: "immutable" blah blah blah "no side effects" blah blah blah Your CTO will be impressed.   
  86. 86. Thanks! @ScottWlaschin fsharpforfunandprofit.com/ettt fsharpworks.com Contact me Slides and video here Let us know if you need help with F#

×