# Domain Design, data- or function-centric?

There are two great articles by Scott Wlaschin on how functional programming can be used for the domain design of real-world applications by implementing a Tic-Tac-Toe game. He demonstrates how business rules can be modeled with types, how data hiding, encapsulation, logging and capability-based security can be achieved with functional programming and more.

What I found remarkable is that in the second article he completely re-engineered the first design. Even though to me the original implementation appeared to be very appealing.

I liked that the data-centric domain model was concise, totally clear and very close to the natural notion of the game. Eventually it couldn’t meet high security standards since there were ways for malicious users of the API to manipulate the data.

The second function-centric implementation introduced the concept of capability-based security. The design smells of the previous version could be resolved. But I argue that the API of the second version is not as intuitive as the first one anymore. Technically it is also quite simple. However, the recursive structure doesn’t come totally natural to me. Also an indicator that the second version is more complex is that logging becomes trickier.

## Why not do both?

I was wondering if it is possible to combine the two approaches? In my opinion this could be achieved by implementing the function-centric version as a thin wrapper around the data-centric version.

Also by doing this the implementation better complies with separation of concerns.

Is it the concern of the domain module to represent the business rules or to ensure security?

### Proof of concept: Secret dice game

Here is a small example as a proof of concept.

I took an even simpler game than Tic-Tac-Toe. The objective is to guess a number (from one to six) that was randomly generated as by rolling a dice. The score will be inversely proportional to the number of trials needed.

### Domain Model

This is the data-centric domain model:

``````module GameDomain =

type Dice = One | Two | Three | Four | Five | Six

type Guess = Guess of Dice

type ValidMoves = Guess list

type GameState = {
Trials: Guess list
Secret:Dice }

type Score = Score of int

type MoveResult =
| Unsolved of ValidMoves
| Solved of Dice * Score

type GameApi = {
NewGame: unit -> GameState * MoveResult
MakeGuess: GameState -> Guess -> GameState * MoveResult }
``````

The types nicely represent the domain and the use cases. Everything is very comprehensive and clear.

### Implementation

The implementation is simple and straight forward:

``````module GameImplementation =
open GameDomain

let private rnd = Random()

let private allpossibleGuesses =
[One; Two; Three; Four; Five; Six] |> List.map Guess

let private newGame ()=
match rnd.Next(1,7) with
| 1 -> One
| 2 -> Two
| 3 -> Three
| 4 -> Four
| 5 -> Five
| 6 -> Six
|> fun dice -> { Trials = []; Secret = dice }, Unsolved allpossibleGuesses

let private isSolved guess secret = guess = secret

let private makeGuess gameState (Guess guess) =
let trials = Guess guess :: gameState.Trials
let score = 6 - List.length trials
let findNextMoves trials =
allpossibleGuesses
|> List.filter (fun guess -> trials |> (not << List.exists ((=) guess)))
let moveResult =
if isSolved guess gameState.Secret then
(gameState.Secret, Score score) |> Solved
else
findNextMoves trials |> Unsolved
{ gameState with Trials = trials }, moveResult

let api = {
NewGame = newGame
MakeGuess = makeGuess }
``````

### Domain Model with capability-based security

Now let’s look at the function-centric domain model which uses capability-based security:

``````module GameDomainWithCapabilityBasedSecurity =
open GameDomain

type MoveCapability = unit -> CbsMoveResult

and NextMoveInfo = {
GuessToMake : Guess
Capability : MoveCapability }

and CbsMoveResult =
| Unsolved of NextMoveInfo list
| Solved of Dice * Score

type CbsGameApi = { NewGame : MoveCapability }
``````

Note that the `GameState` is hidden in this model. However, this post is not about the details of capability-base security. Please refer to this article for a more detailed explanation.

### Implementation with capability-based security

The implementation of the function-centric domain model uses the original implementation and therefore is just a thin wrapper around it.

``````module GameImplementationWithCapabilityBasedSecurity =
open GameDomain
open GameDomainWithCapabilityBasedSecurity

let rec makeMove api moveStatePair =
let (newState, moveResult) =
match moveStatePair with
| Some (guess, gameState) -> api.MakeGuess gameState guess
| None                    -> api.NewGame()

let makeMoveInfo g =
{ GuessToMake = g; Capability = fun () -> makeMove api (Some (g, newState)) }

match moveResult with
| MoveResult.Unsolved validMoves ->
validMoves |> List.map makeMoveInfo |> CbsMoveResult.Unsolved
| MoveResult.Solved (secret, score) -> CbsMoveResult.Solved (secret, score)

let resolveApi api = { NewGame = fun () -> makeMove api None }
``````

The original API is passed as an argument to the functions of this implementation.

### Logging

Logging is pretty easy now because the logger can be injected into the original API:

``````module Logger =
open GameDomain

let injectLogging api =
let makeGuess gameState guess =
api.MakeGuess gameState guess

{ api with MakeGuess = makeGuess }

module ConsoleApplication =

let startGame() =
let loggingApi = Logger.injectLogging GameImplementation.api
let api = GameImplementationWithCapabilityBasedSecurity.resolveApi loggingApi
ConsoleUi.startGame api
``````

### The UI

The complete code including a console based UI is available on GitHub.

Here is a typical course of the game and the output on the console:

``````> ConsoleApplication.startGame();;

------------------------------

USOLVED: Make a guess
0) Guess One
1) Guess Two
2) Guess Three
3) Guess Four
4) Guess Five
5) Guess Six
Enter an int corresponding to a displayed move or q to quit:
2

------------------------------

USOLVED: Make a guess
0) Guess One
1) Guess Two
2) Guess Four
3) Guess Five
4) Guess Six
Enter an int corresponding to a displayed move or q to quit:
3

------------------------------

USOLVED: Make a guess
0) Guess One
1) Guess Two
2) Guess Four
3) Guess Six
Enter an int corresponding to a displayed move or q to quit:
0

------------------------------

SOLVED: Score 3
SECRET: One

Would you like to play again (y/n)?
``````

## Conclusion

Understanding and implementing a data-centric design is easier.

If security is a concern then this implementation can be wrapped inside a function-centric implementation.

To me the approach of combining both designs feels more right because it follows the single-responsibility-principle and the principle of separation of concerns.

The complete code is available on GitHub.