Application structure concerns

I have a nagging suspicion that I have structured my application incorrectly so I’m after some advice to move forward.

Disclaimer: The app I am about to describe is not the app I am working on but for the sake of an example it very closely approximates the layout and desired functionality.

I am running a poker website that allows users to play against each other in real time. Right now I have an umbrella application which has 3 deployable applications made up of 4 code applications - one of which contains all the “shared” functionality. The structure looks like this:

[ umbrella ]
  - [ admin ] - server rendered UI for admins to mange users and games
  - [ api ] - stateless API to drive separate react frontend
  - [ game_server ] - stateful poker processes

  - [ core ] - shared functionality included in all deployable applications

The main reason the apps have been separated is different deploy characteristics:

  • The game server is stateful and can only be run once - requires upgrades for deployment
  • The admin and api apps are stateless and horizontally scalable
  • I don’t want the game server serving API or admin traffic

Originally the codebase was a monolith. To achieve the deploy requirements I conditionally included supervision trees based on configuration. It looked something like:

def MySupervisor do
  def init(config) do
    children = children_from_config(config)
  end

  defp children_from_config(%{type: "game_server"} = config) do
    # ... load game server children
  end
end

While this worked it wasn’t obvious what you were getting and configuration was complicated to say the least - separate applications solved this problem.

In the transition to separate applications I co-located application specific code with each application and extracted anything shared to core. For example, all the processes required to run the poker games live in game server whereas the database schema for the game lives in core as it’s accessed by all three applications.

The reason I think I’ve done the wrong thing.

The other day I had a requirement come through, from the admin UI I want to be able to put put one of the poker game processes into maintenance at the end of a hand. Because the gen server code lives in game server the admin UI isn’t able to send it a message - to solve this problem I used the database as a queue. From the admin UI I wrote a record to the database and the game server checks for new rows at the end of the hand. It works but doesn’t feel elegant. I could move the gen server code into core which would allow the admin server to just call a function on the module and through distributed erlang the process would receive it. But by putting everything in core am I actually any better off than having a monolith and dealing with a little configuration pain?

Any thoughts would be appreciated.

Thanks

4 Likes

This depends if you can bear some players to lose information on potential server restarts (or just plain old outage). Using the DB as a queue is quite fine if you are more conservative about not losing any info.

IMO you have solved some past problems and are now having to deal with others. You did fine. :slight_smile:

Still, you could just use a shared GenServer as opposed to a DB if you feel your solution is clunky. But that wouldn’t change your app’s structure I believe.