0

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.

16
  • 1
    "So if that’s the case, that would imply all my logic is done within a controller" - no, this is not the implication. Your non-UI application logic (business logic) should be on the model side of things (so, this could be a rich domain model, services and data structures, or some combination of the two). 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. Commented Nov 14, 2022 at 20:15
  • @FilipMilovanović thanks. Ok , my problem here however always results in 2 things: 1) It’s harder to implement a Ui. Because I can’t based on conditionals. 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 ! It’s so much easier from the controller to call a view method when/if the time is right to call it. Secondly one class always seems to turn into a God class which does far too much and has too many dependancies. E.g the board class method of takeTurn the has to check 1/2 Commented Nov 14, 2022 at 20:19
  • 1
    Also: "my models are now only suited to the monopoly game" - that's fine, you're not making a generic program, you are making a monopoly game. Being able to use that in a "different application" means that you are able to take that code without dragging the controller/UI stuff along with it, and make another monopoly game application. It doesn't mean that you can make any game whatsoever. You could redesign your model/implementation so that it's more general and can support several different types of monopoly-like board games, but that has nothing to do with MVC separation of concerns. Commented Nov 14, 2022 at 20:22
  • 2/2 if a double is rolled on the dice, it has to then increase a players money, etc etc. whereas in a controller I can just call threes stand-alone methods from seperate classes to interact with each other!? Commented Nov 14, 2022 at 20:25
  • @FilipMilovanović wow. That’s an eye opener. Thanks so much Filip. Why do some developers use data only objects? I can’t understand why, surely that would mean that the data can change from anywhere? Surely that would make our applications hard to bugfix and extremely difficult to read/extend? Achieving some solid Principles here would be difficult? Commented Nov 14, 2022 at 20:30

2 Answers 2

4

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.

9
  • Quite marvellous Filip. Thanks for your answer and congrats on being awesome! Appreciate it! Commented Nov 14, 2022 at 21:22
  • hi Filip. I have done exactly what you have said. I now find myself with a thin controller. A load of small methods from a class and each class seems to have a bigger method ( which is similar to the logic from the controller) does this sound right? Won’t it be difficult to unit test my really long method in a class which calls the other small methods in a class? Thank you Commented Nov 19, 2022 at 4:05
  • @KevinGreetham The "thin controller" part sounds about right, the "really long methods" doesn't. Ideally, the vast majority of methods should end up 2-5 lines of code, but that's sometimes hard to do, and might be unrealistic when you're just learning, so a good thing to aim for would be to fit the whole method on the screen, or something like that. So, look for places where you have lines of code that you've grouped together, maybe with a comment at the top explaining what they are for - and extract that into a higher level method with a name that would make that comment redundant. 1/4 Commented Nov 20, 2022 at 0:41
  • Once you do that, try to extract some of those methods into separate classes that have narrowly defined jobs, and test that. Look for groups of methods that in some sense belong together, that are more closely related to each other than to other methods, or groups that only access a subset of class fields - then you can extract them and those fields into a separate class. Try to arrange things so that you don't have call graphs that are too deep (A calls B calls C calls D calls F...). As for the controller, you don't want to use it to comprehensively test the entire logic of the app, ... 2/4 Commented Nov 20, 2022 at 0:41
  • ... you just want to test if the controller is orchestrating the way it's supposed to (if it's calling the right things at the right times), and not much else (so, not actual results of the business logic). The idea is that some of the things called by the controller are injected as dependencies, and then in your tests, you'd inject mocks instead, and assert if the right methods are called, and/or if your mock-defined ("canned") values are returned. That, together with the tests of the individual classes, gives you confidence that your business logic is correct. 3/4 Commented Nov 20, 2022 at 0:41
0

Originally the Controller in MVC meant an actual device, a keyboard or mouse for example.

Obviously you can press 'B' on the keyboard for 'Buy' or click a 'buy' button with your mouse and they should both do the same thing. So you would want that Buy() method to be on the Model. Property.Buy() or whatever.

When you translate this to websites, (and I'm going to assume your controllers are backend server pages and views are HTML forms?) from the servers point of view its 'Controllers' are the incoming page requests and their handling. Since multiple view can call the same controller its harder to see why you should still keep your Buy method on a model. But we can imagine some complex UI where you have a popup 'Buy property' view with a Button and an alternate, mobile friendly version of the same thing with a 'swipe to buy' interaction. Both are different enough that they might have their own controllers. Each with a 'swipe' or 'click' method, and thus you still want to put the Buy() method on your Model.

To add a further complication, the nature of websites which try to be stateless, with each interaction scoped to a request doesn't lend itself to the standard OOP polymorphic Models with Methods and Data approach.

If your monopoly game was a desktop application, you would have Board with Pieces etc all with their attached methods, you would keep them all in memory and call the methods as the game progressed.

With a web app, the backend has to load the state from a database for every interaction. You have to create and populated all your objects just to call one method on one object and throw it all away again.

This lends itself to a more service orientated design, where you put the methods on services and put only data on your models. Now you can pull just the property from the db, inject your propertyBuying service and call service.Buy(property, player)

In the end its the same code, just organised differently.

6
  • Thanks Ewan. Actually my monopoly game is just a frontend project atm. There is no database. My controller is the direct response of an event rather than an incoming http request. What I’m struggling with I’m struggling with is how to render a view if my logic is done within the model without the code getting messy and ugly aswell as trying to avoid a class which does so much it becomes a god class. Which in this circumstance is my board class. E.g it stores the square objects, it checks the square objects, checks the dice score, changes properties and methods of it it’s dependancies 1/2 Commented Nov 14, 2022 at 20:53
  • 2/2 by calling methods or each dependancy. It’s just does so much !! Commented Nov 14, 2022 at 20:57
  • I think you need to provide some example code of how you are applying MVC in a pure front end application. You should be binding your models to the views via a framework, react? vue? angular?
    – Ewan
    Commented Nov 14, 2022 at 21:48
  • Ewan. Hi. Thanks for taking to time out to assist. My code is a bit all over the place atm as I’m refactoring. My model is every object in the game. E.g Dice, Board (inside the board I have squares) each square is an object e.g PropertySquare , ChanceSquare etc. the state of each square is updated. The Board class that holds the squares has methods e.g to find the total value of the current players wealth to see if they can buy a property that they land on. Now originally I called each method when I needed to from my controller and done the logic there and then simply called methods from 1/2 Commented Nov 14, 2022 at 21:57
  • Different UI classes when the time is right to do so. E.g if the property is free then I call a ui method to show this. No frameworks uses, simply Js and the DOM were used to show the UI and the current state of all the players in the game. The game worked perfectly. However I ended up with a huge controller and a bunch of switch statements. So I’m refactoring to more SOLID design e.g instead of getting the type of square landed on and then running a load of logic in My controller I am using polymorphism within my Model and the controller just calls for the square and activates 2/3 Commented Nov 14, 2022 at 22:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.