Skip to content

Instantly share code, notes, and snippets.

@yreynhout
Created June 22, 2016 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yreynhout/535a0b9f5960e568659af8e6284b7fcc to your computer and use it in GitHub Desktop.
Save yreynhout/535a0b9f5960e568659af8e6284b7fcc to your computer and use it in GitHub Desktop.
open System
module PlayerAccount =
//PlayerWasInvited
//PlayerAcceptedInvitation
//PlayerRejectedInvitation
//PlayerInvitationWasCancelled
//
type ProfessionalOccupation = Employee | Independent | Labourer | Other
type SignUpDocument = {
DocumentId: Guid;
PlayerId: Guid;
NickName: string;
InitialPasswordHash: string;
InitialPasswordSalt: string;
EmailAddress: string;
FirstName: string;
LastName: string;
PlaceOfBirth: string;
ProfessionalOccupation: ProfessionalOccupation;
OtherProfessionalOccuptation: string option;
HasBelgianIdentityCard: bool;
BelgianNationalNumber: string option;
}
type PlayerSignedUp = { PlayerId: Guid; SignUpDocumentId: Guid; NickName: string; EmailAddress: string; At: int64 }
type PlayersIdentityConfirmed = { PlayerId: Guid; VerificationDocumentId: Guid; At: int64 }
module PlayerWallet =
type GiveWalletToPlayer = { WalletId: Guid; PlayerId: Guid; At: int64 }
type PutMoneyInPlayersWallet = { WalletId: Guid; Amount: decimal; At: int64 }
type PutBonusMoneyInPlayersWallet = { WalletId: Guid; Amount: decimal; Until: int64; At: int64 }
type TakeMoneyOutOfPlayersWallet = { WalletId: Guid; Amount: decimal; At: int64 }
type TradePlayersWallet = { OldWalletId: Guid; NewWalletId: Guid; At: int64 }
type Commands =
| GiveWalletToPlayer of GiveWalletToPlayer
| PutMoneyInPlayersWallet of PutMoneyInPlayersWallet
| PutBonusMoneyInPlayersWallet of PutBonusMoneyInPlayersWallet
| TakeMoneyOutOfPlayersWallet of TakeMoneyOutOfPlayersWallet
| TradePlayersWallet of TradePlayersWallet
type WalletWasGivenToPlayer = { WalletId: Guid; PlayerId: Guid; At: int64 }
type MoneyWasPutInPlayersWallet = { WalletId: Guid; PlayerId: Guid; Amount: decimal; At: int64 }
type BonusMoneyWasPutInPlayersWallet = { WalletId: Guid; PlayerId: Guid; Amount: decimal; Until: int64; At: int64 }
type MoneyWasTakenOutOfPlayersWallet = { WalletId: Guid; PlayerId: Guid; Amount: decimal; At: int64 }
type PlayersWalletWasTraded = { OldWalletId: Guid; PlayerId: Guid; NewWalletId: Guid; Balance: decimal; At: int64 }
type Events =
| WalletWasGivenToPlayer of WalletWasGivenToPlayer
| MoneyWasPutInPlayersWallet of MoneyWasPutInPlayersWallet
| BonusMoneyWasPutInPlayersWallet of BonusMoneyWasPutInPlayersWallet
| MoneyWasTakenOutOfPlayersWallet of MoneyWasTakenOutOfPlayersWallet
| PlayersWalletWasTraded of PlayersWalletWasTraded
type Conflict =
| PlayerHasAlreadyBeenGivenWallet
| PlayerWalletMismatch
| PlayerWalletHasInsufficientFunds
| PlayersWalletAlreadyTraded
type PlayerWalletState = { WalletId: Guid; PlayerId: Guid; Balance: decimal; TradedWithWalletId: Guid option }
with
static member Empty = { WalletId = Guid.Empty; PlayerId = Guid.Empty; Balance = 0.0m; TradedWithWalletId = None }
static member Fold (events: Events list) =
let folder (state: PlayerWalletState) (event: Events) =
match event with
| WalletWasGivenToPlayer e -> { state with WalletId = e.WalletId; PlayerId = e.PlayerId; }
| MoneyWasPutInPlayersWallet e -> { state with Balance = state.Balance + e.Amount; }
| BonusMoneyWasPutInPlayersWallet e -> state
| MoneyWasTakenOutOfPlayersWallet e -> { state with Balance = state.Balance - e.Amount; }
| PlayersWalletWasTraded e -> { state with TradedWithWalletId = Some e.NewWalletId; }
List.fold folder PlayerWalletState.Empty events
// A wallet is given to a player just after he signs up and we've confirmed the player's identity, by our system.
let giveWallet (command: GiveWalletToPlayer) (state: PlayerWalletState) : Choice<Events list, Conflict> =
if state <> PlayerWalletState.Empty then
Choice2Of2 PlayerHasAlreadyBeenGivenWallet
else
//Is this a player we know of?
//Is this a new player that does not yet have a wallet?
Choice1Of2 [ WalletWasGivenToPlayer { WalletId = command.WalletId; PlayerId = command.PlayerId; At = command.At } ]
let putMoney (command: PutMoneyInPlayersWallet) (state: PlayerWalletState) : Choice<Events list, Conflict> =
if state.WalletId <> command.WalletId then
Choice2Of2 PlayerWalletMismatch
else
match state.TradedWithWalletId with
| Some _ -> Choice2Of2 PlayersWalletAlreadyTraded
| None -> Choice1Of2 [ MoneyWasPutInPlayersWallet { WalletId = state.WalletId; PlayerId = state.PlayerId; Amount = command.Amount; At = command.At } ]
let takeMoney (command: TakeMoneyOutOfPlayersWallet) (state: PlayerWalletState) : Choice<Events list, Conflict> =
if state.WalletId <> command.WalletId then
Choice2Of2 PlayerWalletMismatch
else
match state.TradedWithWalletId with
| Some _ -> Choice2Of2 PlayersWalletAlreadyTraded
| None ->
if state.Balance - command.Amount < 0.0m then
Choice2Of2 PlayerWalletHasInsufficientFunds
else
Choice1Of2 [ MoneyWasTakenOutOfPlayersWallet { WalletId = state.WalletId; PlayerId = state.PlayerId; Amount = command.Amount; At = command.At } ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment