Been lurking for awhile, first time posting. I’ve checked for guidelines briefly, but please let me know if I should move this elsewhere, or format differently, etc.
The last few days I’ve been toying around with Elixir and OTP. As a little project to learn with, I’ve been porting a turn based card game I wrote in Ruby to support multiplayer gameplay.
I have a bare bones version of it up and running but I’m looking for some feedback on the design.
Here’s a rough outline of the current iteration of my OTP application:
/application /Registry /GamesSupervisor (DynamicSupervisor) /GameSupervisor (Supervisor) /GameServer (GenServer) /PlayersSupervisor (DynamicSupervisor /PlayerServer (GenServer)
At a super high level, as you can probably guess, the application starts up the
GamesSupervisor is responsible for creating new games via
GamesSupervisor.create_game(<game_id>). This is done by starting a
GameSupervisor, which starts a
GameServer that gets registered using the
GameServer holds the state of the game (turn, board state, current choice, etc).
GameServer exposes a
join_game(<pid>, <player_name>) “call” function that tells the
PlayersSupervisor to start a
PlayerServer. Many players can join a game, and the
PlayerServer is used for bidirectional communication between the game server and the individual players (e.g presenting choices and making decisions).
My high level goal is to isolate failure (in this case bugs in the game) to an individual game. For instance, if the game erroneously allows the player to choose an invalid choice (according to the game logic) it would not impact other games.
The first thing I’ve gotten hung up on is where the
PlayersSupervisor should go in the hierarchy. My intuition tells me it should be a child of
GameSupervisor because the
PlayerServer processes that it ends up starting are conceptually part of the game?
However, it’s not clear to me if there is a benefit to nesting the dynamic
GameSupervisor. I’ve not come across any examples of folks having multiple instances (processes) of a dynamic supervisor. Seems possible, just requiring a more dynamic process name to avoid the “already started” error.
Is it common to use the registry to registry multiple types (for lack of better word) of processes or would you create multiple named registries? For example, registering processes that represent my
PlayerServer within the same registry.
From the reading I’ve been doing, it seems common to expose a function on your supervisor as the entry point (e.g
While this makes sense to me, I’m currently planning on keeping the
GamesSupervisor interface small. Instead, I’m planning to interact with the
PlayerServer processes directly, relying on their processes being locatable via the registry.
So I guess my question is around convention; do folks tend to use the supervisor as the interface to dispatching to the processes it supervises? Maybe this is just personal preference?