# DDD - Interaction between 2 entities

I've been taking a crack at Domain Driven Design, SOLID principles, and unit tests to write more maintainable code in the future.

Though I tend to run into the same issue: While I have many classes/objects that are easy to test, and do one thing well, I'm completely stumped on how to get classes to interact with each other elegantly.

For example, an Arcade domain, where players spend tokens to play games. The following code will be in TypeScript but I'd imagine this applies to other languages.

• First I have a class (Value Object) to keep track of Token balances:
    //Value Object: Keeps track of a bag of tokens.
class TokenBalance {
constructor(balance: number) {
if(balance < 0) {
throw new Error('Token balance must be 0 or higher');
}
this.balance = balance;
}

return new TokenBalance(this.balance + amount);
}

RemoveFromBalance(amount: number) {
}
}

• Next I have a player (Entity), which has a name, and holds a balance of tokens
    //Value Object: Player Name
class PlayerName {
constructor(name: string) {
if(!name.length) {
throw new Error('Name cannot be blank')
}
this.name = name;
}
}

//Entity (Aggregate Root?): Holds a name and tokens
class Player {

private tokens:TokenBalance;

constructor(tokens: TokenBalance, name: PlayerName, id:string) {
this.id = id;
this.tokens = tokens;
this.name = name;
}

GetTokens() {
return this.tokens;
}

DepositTokens(amount: number) {
}

WithdrawTokens(amount: number) {
this.tokens = this.tokens.RemoveFromBalance(amount);
}
}

• A simple guessing game class (Entity as well?) to keep track of Game State (No logic regarding players at this point)
    enum GameState {
IN_PROGRESS,
LOSS,
WIN
}

//Entity?: Maintains invariants of guessing game logic
class GuessingGame {
public static MAX_GUESS_COUNT = 5;

private correctNumber:number;
private guesses: number;
private state: GameState;

constructor(correctNumber: number, guesses: number, state:GameState, id:string) {
if(guesses < 0) {
throw new Error('Negative guesses are impossible');
}

this.id = id;
this.correctNumber = correctNumber;
this.guesses = guesses;
this.state = state;
}

public GetState() {
return this.state;
}

public Guess(number: number) {
if(this.state !== GameState.IN_PROGRESS){
throw new Error('Game is over');
}
this.guesses++;
if(this.guesses >= GuessingGame.MAX_GUESS_COUNT){
this.state = GameState.LOSS;
}
if(this.correctNumber === number) {
this.state = GameState.WIN;
}
return this.state;
}

public GetGuessCount() {
return this.guesses;
}
}

• Finally, the class that I'm most unsure about. The mediator(?) of Games and Players to be used when a player wants to play a game:
    //Domain Service?: Mediates the interactions between a Player and a Game. Ensures that tokens are used before playing a game
//How can this be retrieved later from a repository when it holds references to 2 entities. How would this be saved to a repository
class PlayerGuessingGameSession {
private player: Player;
private game: GuessingGame;
private hasStarted: boolean;
private tokenCost:number;

constructor(player: Player, game: GuessingGame, tokenCost = 50, hasStarted = false) {
this.player = player;
this.game = game;
this.hasStarted = hasStarted;
this.tokenCost = tokenCost;
}

public StartGame() {
if(this.hasStarted){
}
this.player.WithdrawTokens(this.tokenCost);
this.hasStarted = true;
}

public Guess(number: number) {
if(!this.hasStarted) throw new Error('No tokens inserted, cannot start');
const state = this.game.Guess(number);
if(state === GameState.WIN){
this.player.DepositTokens(this.tokenCost + 5); //Reward the user with 5 additional tokens
}
}
}

//Putting it all together

const player = new Player(new TokenBalance(100), new PlayerName('Steven'), 'player_id');
const game = new GuessingGame(5, 0, GameState.IN_PROGRESS, 'some_id');
const gameSession = new PlayerGuessingGameSession(player, game);

console.log(player.GetTokens()); // TokenBalance { balance: 100 }
gameSession.StartGame();
gameSession.Guess(5);
console.log(player.GetTokens()); // TokenBalance { balance: 105 }


As the comments may have pointed out, I have a lot of concerns about the last class. If a Player starts a game, leaves, and comes back to continue, I would need a way to retrieve this session from some storage system (repository). Unfortunately, the session contains a Player and a Game entity, so saving a session may cause side effects to the Player and Game entities.

How can I ensure that a game can only be played after tokens are spent, reward players on a win, and give the ability for a player to continue a session later?