Slide 1

Slide 1 text

Enterprise Tic-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

Slide 2

Slide 2 text

Enterprise Tic-Tac-Toe

Slide 3

Slide 3 text

What you need for “Enterprise”? • Siloed organization Specialized teams • Architecture Team • Project Management Team • Front End Development Team • Back End Development Team • Operations Team • Security Team • Compliance and Auditing Team Can we make them all happy?

Slide 4

Slide 4 text

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.

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

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.

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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.

Slide 11

Slide 11 text

I’m gonna use F#, so here’s all the F# you need

Slide 12

Slide 12 text

type Pair = int * int "*" means a pair type Payment = | Cash | Card of CardNumber "|" means a choice type Thing = { name:string } "{" means a record

Slide 13

Slide 13 text

type AFunction = string -> int A Function 42 “Deep Thought”

Slide 14

Slide 14 text

A Function type AFunction = string -> int * bool Input is a string Output is a pair

Slide 15

Slide 15 text

A Function type AFunction = bool * string -> int Input is a pair Output is a int

Slide 16

Slide 16 text

TYPE DRIVEN DESIGN

Slide 17

Slide 17 text

Growing functional software, guided by types

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

Tic-Tac-Toe Scenarios • Initialize a game • A move by Player X • A move by Player O

Slide 20

Slide 20 text

type StartGame = unit -> GameState StartGame Game State nothing

Slide 21

Slide 21 text

OtherStuff Player X Moves type PlayerXMove = GameState * SomeOtherStuff -> GameState GameState (before) GameState (after) Before After Loop

Slide 22

Slide 22 text

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.

Slide 23

Slide 23 text

UserAction Any Kind of Move type UserAction = | MoveLeft | MoveRight | Jump | Fire GameState (before) GameState (after) Generic approach

Slide 24

Slide 24 text

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....

Slide 25

Slide 25 text

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!

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

type PlayerXMove = GameState * CellPosition -> GameState type PlayerOMove = GameState * CellPosition -> GameState Same again 

Slide 28

Slide 28 text

type PlayerXMove = GameState * PlayerXPos -> GameState type PlayerOMove = GameState * PlayerOPos -> GameState type PlayerXPos = PlayerXPos of CellPosition type PlayerOPos = PlayerOPos of CellPosition Different functions Different positions

Slide 29

Slide 29 text

What is the GameState? type GameState = { cells : Cell list } type CellState = | X | O | Empty type Cell = { pos : CellPosition state : CellState }

Slide 30

Slide 30 text

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!

Slide 31

Slide 31 text

What is the Output? What does the UI need to know? The UI should not have to "think" -- it should just follow instructions.

Slide 32

Slide 32 text

What is the Output? 1) Pass the entire game state to the UI? But the GameState should be opaque...

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Time for a walkthrough... Start game Player X moves Player O moves Player X moves Player O moves Player X moves Player X wins!

Slide 36

Slide 36 text

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?

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Review

Slide 39

Slide 39 text

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!     

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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 

Slide 42

Slide 42 text

Some refactoring (before) type GameStatus = | InProcess | Won of Player | Tie type PlayerXMove = GameState * PlayerXPos -> GameState * GameStatus * ValidMovesForPlayerO Merge into one type

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Time for a demo!

Slide 45

Slide 45 text

Hiding implementations with Parametric Polymorphism

Slide 46

Slide 46 text

Hiding implementations with Parametric Polymorphism

Slide 47

Slide 47 text

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)

Slide 48

Slide 48 text

Enforcing encapsulation • OO approaches: – Represent GameState with an abstract base class – Represent GameState with an interface – Make constructor private

Slide 49

Slide 49 text

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, 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.

Slide 50

Slide 50 text

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.

Slide 51

Slide 51 text

Logging

Slide 52

Slide 52 text

move We want to log the input and output. But how? Logging

Slide 53

Slide 53 text

move Logging log Step 1: Create a log function

Slide 54

Slide 54 text

move Logging log Step 2: glue all the functions together using composition log

Slide 55

Slide 55 text

move Logging log Step 2: glue all the functions together using composition log

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Demo of logging

Slide 58

Slide 58 text

Client-server communication How do you send domain objects on the wire?

Slide 59

Slide 59 text

JSON over HTTP? Enterprise Rating: C-  What communication method should we use?

Slide 60

Slide 60 text

XML & SOAP? Enterprise Rating: A  What communication method should we use?

Slide 61

Slide 61 text

Enterprise Service Bus! Enterprise Rating: A++  What communication method should we use?

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

Demo of problems

Slide 64

Slide 64 text

Stupid people Evil people What’s the difference? 

Slide 65

Slide 65 text

POLA & Capability Based Security

Slide 66

Slide 66 text

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?

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Attempt 2 Give the caller a TextWriter interface IConfiguration { TextWriter GetConfigWriter(); } var writer = config.GetConfigWriter(); // write new config API Caller  A malicious caller can corrupt the config file

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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?

Slide 71

Slide 71 text

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.

Slide 72

Slide 72 text

Good security implies good design

Slide 73

Slide 73 text

Good security is good design • Filename => limit ourselves to file-based config files. – A TextWriter 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!

Slide 74

Slide 74 text

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!

Slide 75

Slide 75 text

Attempt 5 A one method interface is a function interface IWarningMessageConfiguration { void SetMessageFlag(MessageFlag value); } OO API Action messageFlagCapability Functional API

Slide 76

Slide 76 text

Capability Based Security and Tic-Tac-Toe

Slide 77

Slide 77 text

Switching to cap-based Tic-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

Slide 78

Slide 78 text

Cap-based Demo

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

How NOT to do HATEOAS POST /customers/ GET /customer/42 If you know the API you’re doing it wrong

Slide 81

Slide 81 text

How to do HATEOAS POST /81f2300b618137d21d / GET /da3f93e69b98 You can only know what URIs to use by parsing the page

Slide 82

Slide 82 text

HATEOAS Demo

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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   

Slide 85

Slide 85 text

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.   

Slide 86

Slide 86 text

Thanks! @ScottWlaschin fsharpforfunandprofit.com/ettt fsharpworks.com Contact me Slides and video here Let us know if you need help with F#