We’re moving a game over from Meteor to Elixir.
It’s a fantasy football draft, so basics are that users pick players one at a time with 30 seconds per pick. If you pick a player no one else can pick him.
I started implementing it by mostly just doing what we’re doing in Node.js (Meteor), but as I learn Elixir better I’m rethinking the structure.
We have lots of leagues that each contain a group of lets say 10 users. I think this would be implemented as a GenServer with each server representing a different league. This server would keep track of which players have already been picked, who is in the league, the timer, etc. Anything to do with the league.
Firstly, does this structure make sense?
And assuming it does, when would I access the database? I’d be able to store all the data in memory for a given league while it’s drafting, would I then only update the database but only read from it on startup? And I’d update the database each time the in memory data gets changed?
Is this the standard way to do things in Erlang/Elixir? Mostly staying away from the database?
I believe it’s too vague description to give you some solid suggestions, but I do think that having one GenServer per league will result in large single file with a lot of code in it.
In addition to that, note that GenServer does one thing at a time. Most likely you need a tree of processes that exchange messages and follow single responsibility principle as much as possible. Have one to save / read state from database. Have another one that is responsible for timer. Another one that tracks some stats etc.
Think this way: the GenServer abstraction is used to have either state and behavior. So, for example if you want to store information about the league I would have a callback (either a call or cast). If you need to get data when the genserver starts the init callback is the best place to do it, since it’s most for initializing things.
Your case to have a single GenServer for each league is a good approach. But remember that state in a GenServer is ephemeral by default, so you will need a recovery strategy for your data in case of failure. You could do something like Event Sourcing for that, if you are not familiar with the concept, bascically you store all the events that happened so, in case of failure you can simply reconstruct the state by replying these events.
Instead of event sourcing why not just save the state to a database?
@hubertlepicki yes, my logic is split into multiple files. But the basic functionality for each league I’d hope to stick into a GenServer, spawning a new one for each league.
In my experience, Elixir in Action covers a lot of useful ground when it comes to this type of question; for example, you get to follow todo lists all the way from an initial, simple data abstraction to an OTP-based system with caching and persisted data.
Of course you can always save directly into the db the state.
I suggested event sourcing in this case, because the app architecture allows this in an easier and elegant fashion. And, of course, there are many advantages such as: replayability, history of changes (although some databases do that), auditing…