JavaScript MVC – How to Achieve Polymorphism in Controller Logic

javascriptmvc

First, let me start by saying I’m a JavaScript developer but any conceptual advice I’d imagine is identical if not similar to when dealing with MVC.

During my learning, I stumbled across MVC and now most of the projects I do are implemented in this pattern. Even if it’s overkill sometimes I still do it to learn the best practices around it.

I have seen and read both sides of the dispute regarding where application logic should take place. I’ve heard we should have thin controllers which basically calls a method on the model and the logic of what should happen to our data happens inside this method. I’ve also seen and used Dependency Injection in various projects where instantiated objects are passed either as parameters of a method or in some cases as properties of a constructor. This helps to achieve polymorphism.

I have tried both thin controllers and placing logic throughout the model, and fat controllers where I complete the logic in the controller which does the same thing as the logic inside the method. I find both have advantages and disadvantages.

My question is this:

I have seen in various places that “if you can’t put your models on another application and the controller of that application calls methods from this model inside another controller, then you're probably doing it wrong.” These are experienced and respected developers making this point.

So if that’s the case, that would imply all my logic is done within a controller calling methods to interact with each other inside my controller. However, how am I then meant to achieve polymorphism?

E.g.:

I have a game of monopoly when the current player lands on a square I can do one of two things –

  1. get the type of the square landed on and run some logic.

    If(square.type === “property”) {
        //check if owned
        //if it’s not owned call a view method giving us the option to buy
    }
    

    I would then have to repeat this if for if the square type is a chance, community chest, fine etc. It's clearly not clean code.

  2. Instead my square object uses polymorphism which takes in dependencies when being constructed. Each square has an activateLogic() method. Meaning the exact same logic from my controller is now in my Model. Yet some say this is wrong, app logic should be in the controller, my models are now only suited to the monopoly game because I have passed in various dependencies to it e.g. the current player, and the dice (to check if there is a double). But then others say my code is cleaner in the model, less switch statements, etc. e.g. I only simply have to get the square in my controller and call activateLogic() on the square and polymorphism takes care of the rest.

Going back to the main point of the question – if my app logic is in the controller, how am I ever going to achieve polymorphism? Surely at some point, I am going to have to complete most of the logic in my model.

Best Answer

Context: From some of your comments, it seems like you're dealing with what's basically a desktop application running on JavaScript, so this is mostly written in that light (that is, the scenario is not Web-MVC).

"I have seen in various places that “if you can’t put your models on another application and the controller of that application call methods from this model inside another controller, then your probably doing it wrong.” These are experienced and respected developers making this point."

[...]

"Meaning the exact same logic [that was previously in] my controller [now resides] in my Model. Yet some say this is wrong, app logic should be in the controller, my models are now only suited to the monopoly game" [emphasis by the answerer]

So, the point that is being made shouldn't be taken too literally, cause what's meant by "another application" is a bit more technical than it appears.

It's absolutely fine that your domain model is only suited for making monopoly games. Remember, you're not making some general-purpose programmable environment (that's what programming languages exist for), you are making a monopoly game.

Being able to use that in a "different application" means that you have arranged things in a way that allows you to take that code without dragging the controller/UI stuff along with it (and, potentially, any other externalities, like a database) - and make another monopoly game application (perhaps with better graphics). So, it's the ability to just extract the very core that encodes the logic of the monopoly game (note: not necessarily easy to achieve, you have to think it through, and it might take several iterations). That core doesn't have to be functional by itself (doesn't have to be runnable out of the box), it just needs to capture within itself the rules of the monopoly game, and allow you to "plug in" (or build around it) different components (such as a GUI) that together with it make a complete application.

It doesn't mean that you are able to make any game whatsoever. Now, you could redesign your model/implementation so that it's more general in nature, so that it's able to support several different types of monopoly-like board games, but that's just domain modeling (you're re-conceptualizing the core problem itself). It's a separate issue from the MVC separation of concerns.

"So if that’s the case, that would imply all my logic is done within a controller"

In light of the above, this is not the implication. Your non-UI application logic (business logic) should be on the model side of things (this could be a rich domain model, services and data structures, or some combination of the two).

One way to think about is that, you should, in theory, be able to take that and attach a differently conceptualized UI to it (say, terminal-based, instead of a GUI) - think of that as a "different application" as an initial approximation.

"E.g. if property is for sale the create a popup box asking if user wants to buy property? Achieving that is hard if my logic is in the model !"

Now, note that what's discussed above doesn't mean that you should make your domain objects magic black boxes that do everything, and somehow shoehorn UI logic in there as well.

You'd still call and orchestrate stuff from the controller, you'd just be calling higher level methods on the domain objects (or services) themselves, instead of manipulating every detail on the spot. If implemented correctly, your controller code would end up being simpler, and you'd almost be able to read the method calls in it as if it were a list of bullet points - a list of high level steps describing the purpose of the method, rather than a wall of generic program instructions that you need to make sense of when you come back to the code 3 months later.

E.g. you wouldn't ask your model to create a popup, you'd create a popup in the controller (or in whatever UI code is responsible for that), and pass a domain object to the popup so that it calls methods on it (like buy(...)) it or get information from it (e.g., you'd call getPropertyDescription(...), and populate the fields with what that returns), or something along those lines (in an actual application, the logic might be a bit more involved, but the basic idea is the same).

Also remember that you can (manually or via a DI container) inject polymorphic objects and/or lambdas into some of your domain objects. E.g. an application-level domain object can accept, as a parameter to a method, an object or a lambda that allows it to send over the result of some calculation when the method computes it. You can pass it (or inject) code that takes in some data and updates something in the UI - the object doesn't know anything about the UI because it's just passing a computation result, and the handler comes from an external source.