'How to handle too much logic in the controller vs the models calling each other

I encountered a code design problem while working on a small application.

In terms of features, there are a list of tables, each having 2 seats. If two players sit down at the same table, then a game starts.

For this part, I have a tables controller, a table model, and a game state model (creating a game state means a game has started).

When a user sits down, the code fires an Ajax request which is handled by the tables controller, which calls the appropriate method in table model for sitting. If the table model finds out both seats are filled, the game starts, which is the tricky part.

I didn't want to have the table model call game state model because it feels messy and tracking who is calling the game state model may become difficult later on. So I made the table model return a :success=>true hash to table controller, which determines whether to call game state model.

But then I realize I'm putting logic in the controller, which according to Rails 3 Way, is a no-no.

Can someone tell me what I can do better?

I'm also having problem with "forfeit the game if user disconnects part". Currently the user pulls the table controller in order to let my app know hes still connected. And having that part handle game forfeit seems awkward and coupling.

Additionally, I'm making the JavaScript code do one setInterval pull for each type of resource, in an attempt to keep things modular. But as a result, I'm making 6-7 different AJAX requests every interval. And that seems inefficient.



Solution 1:[1]

First we need to decide which models know about which other models. In our case, we can probably say something like

GameState -> Table -> User

Which means that the model knows everything to the right of it, and knows nothing about the model to the left. This way, we can more easily determine where a lot of logic naturally belongs to, because a User model doesn't know anything about the Table model itself, it just knows that it belongs to a Table.

Now, let's think about the different "states" we have in a game.

  1. Pre-game state where a table being waiting to be filled
  2. Game state, and of course this will a lot of sub states
  3. After game, scores are counted, variables are updated

With regards to #1, our first guess that this belongs to the Table. It should only know about itself and the state that itself is in. But it's only role is that it has two seats, and it can be filled. It shouldn't know about when a game can be started or anything. What does this mean? We actually need to delegate the job to the GameState, because the pre-game is also a "state" of the game. The GameState would be the "gatekeeper" if you will, and the Table is just a pawn. With that being said, it'll be a good idea to have your GameState model call your Table model to SEE it can go ahead and start the game. When a user clicks to join a table, it will go to the GameState controller (make sure your logic belongs in the model, so that your controller is just calling methods in your model). The GameState controller will attempt to add this User to the Table and see if it can start a game (the table merely returns if all seats have been filled). If so, it send the right information back to the client and says, "Okay! Start!".

Once the game has started, then it's up to the GameState to manipulate itself and the data that belongs to it (the Table and Users if need be). Once the game is finished, GameState cleans itself up (along with it's members) and archives itself into the db. So all-in-all it would seem like GameState overlooks the whole process, and Tables/Users are just data that GameState manipulates.

With regards to user disconnecting, it can be hard to say what's the right thing to do without much context. But if I were to make something like this, it doesn't seem like you need polling. What I can think of is a user either navigates away from the page (either closing the browser, typing new url, or clicking on a link), you can use unload() to send a request to the server telling you that the user left. Another way would be if a user clicks "disconnect", which is also another request sent to the server.

In terms of having to send 6-7 AJAX requests every interval seems a little excessive. If you really want to, you should package up all your resources into one object, send one object every interval, let the server manipulate it, then handle it when the object comes back. But it would also seem like you need not all these polling. The only thing you need to do is validation, and encryption if you want, to make sure that GameStates are transitioning in a legal way.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TylerH