4 ms·
The M in MVC: Why Models are Misunderstood and Unappreciated
- whalesalad 15y agoYES! I am exhausted from a long run and cannot articulate my thoughts clearly but just as an example, I try and overload my Django models with as much logic as I possibly can. Add tons of functions for fetching attributes, use @property(s) and make use of custom managers or queryset subclassing when you can. If you spend the time to "fatten" (as this article mentions) up your models, your views and the actual nitty-gritty glue holding your app together will be MUCH simpler and easier to follow. Let your models do the heavy lifting!
- badclient 15y agoNice post that would be even nicer if accompanied by some pseudocode examples. You go on and on about constantly reducing the size of your controllers. I'm sold! I just don't know where to start :( Let's say I am verifying login. So I have the controller that looks something like this: Function logincontroller() { if (model->verify(user,pass)) redirect(validurl) Else Redirect(invalidurl) } How can I improve this? Btw my intended syntax was php but left out proper syntax due to iPhone:)
- bergie 15y agoThat looks pretty much correct: * Model handles its own logic (in this case, verification of credentials) * Controller controls what happens (calling model to verify, redirecting user) In general, models should only be concerned about their own data, and about their relations to other models. All behavior related to users should be handled by controllers (that call models as needed).
- badclient 15y agoThanks for the reply! Let's say I add a few more things to that controller: namely ability to check if account is disabled(and to disable after x attempts and notify user) or if a captcha needs to be shown or was shown etc. I feel before you know it the controller can get fairly complicated with all the asserts in real-life scenarios. May be the orig author is ok with it but reading the post I started getting ideas about simple 5-10 line controllers.
- bergie 15y agoDisabled accounts should be checked in the model. But instead of returning a boolean, it could throw appropriate exceptions (WrongPasswordException, AccountDisabledException) that you can then handle in the controller.
- glenjamin 15y agoThis would be my preferred approach, you can even just key translations based on the name of the exception, then handle them all with a generic flash message / template.
- Volpe 15y agoReally? Throwing exceptions on known states of your model... hardly exceptional.
- glenjamin 15y agoHow else would you achieve it - throwing exceptions for program flow is quite common in Python for example; Iterator.next() throws StopIteration when it reaches the end of the iterable.
- Volpe 15y agoThat's an exceptional circumstance, the iterator is not responsible for knowing whether you have reached the end of the collection. You could achieve it by catering for these cases in your logic if user.authorize(email, pass): ... success ... else: ... fail ... rather than try: user.authorize(email, pass) ... success ... except WrongPasswordException: ... fail ...
- glenjamin 15y agoI see your point, I didn't notice that the parent mentioned WrongPasswordException, when I implemented something similar previously it looked like this: User.authorize(username, password) -> Returns true if valid username/password -> Returns false if username/password do not match -> Raises AccountDisabled if username/password valid but account disabled The normal login failure case is not exceptional - but the others are.
- smackay 15y agoFat Stupid Ugly Controller instead of the succint (decidedly more literate) Fat Controller - sadly an opportunity lost.
- BSousa 15y agoThis is probably my nitpick with people (not all) that come from Rails and tries to 'teach' others about general concepts by the way they are done in Rails. A good example is "In Programming, Fat Models Are Preferable To Size Zero Models". Sure, if you are in Rails, you probably don't want a 3000 lines controller, but guess what, there are also Services! By moving logic to the Model you end up with fat models that do a lot of things, instead of having specialized Services for logical parts of you application. badclient example below is a prime example. Do we really want the model to handle authorization? What if it also needs to know about the account the user is trying to access has been suspended (then the User model needs to know about Account model suspension) or even authorization for that specific page (only managers should be able to access payroll). By moving the logic to the model, you now have a big fat model that needs to know about everything in the application (and most likely, a lot of logic will have to be replicated throughout other models). About the "Fat Stupid Ugly Controllers" can also be separated to View Presenters, making the code much cleaner (presentation is presentation, controller is controller, model is model) and not Fat Stupid Ugly Models. Sorry for the rant, but I'm sick and tired Rails programmers talking about Rails ways like it is the one true way while there are a lot of Software Design Practices that are better suited for each case.
- mattmanser 15y agoI don't use Ruby, I do use MVC (C# in my case). I honestly don't agree with your interpretation of 'MVC'.
- BSousa 15y agoIt wasn't an interpretation of MVC per-se but more of what I think the Model in any architecture should be. The Model can be used in MVVM and MVP as well and can be part of DDD way of doing things. And again, for me, a model should be a representation of an object/data. If it is tied to an ORM (Active Record or Entity Framework) it may come with DB access, but I still don't think models should contain application specific logic. That's what the 'Business' layer should be for. If an Invoice model knows about tax codes from 130 different countries, again I think it knows way too much about the application.
- lifeisstillgood 15y agoI am concerned by the tendancy to throw everything into one in-process model. (by in-process I mean that we are calling a ruby function calculatePrice in the same model / whatever instead of calling out to a web api and getting back a price) It's usually cleaner to think about web service calls - not put everything into same model even though you can cleanly seperate functions within a single model the tendancy to make an inprocess call not an api call is always high. So removethe tempation. Have a thin model, a thin view and a thin controller all doing just one (rest-ful) thing and call between them the overhead in creating objects from passed in Json is far lower the trying to unstitch logins, transactions, funny bits of business logic from one monolithic model (not that I am saying you would end up with a monolithic piece of spagetti but I have seen some indark corners of the enterprise world, even in projects which have all the right buzzwords)
- Swannie 15y agoI struggled to comprehend what you are trying to say. It sounds like you're saying "Make everything a service and that will solve all your problems". But in doing so, you abstract the complexity from the core of the problem, to some other layer. Sort of like SOA, meaning you end up with some orchestration language (BPEL?). You've ended up with a kitchen full of self-replicating spaghetti.
- dataduck 15y agoSlightly OT extreme noob question: what is the core idea behind controllers? I've read some introductory Rails stuff but I never really got a good grasp of what the point of the controller is, most explanations seemingly reducing to "the thing that goes between the thinky bit and the user interface". I know I'm missing a lot, can anyone point me in the direction of some good reading material? My background isn't webdev so there's probably some missing assumed knowledge.
- ovi256 15y agoThe point of controllers would be to glue together front-end protocol (HTTP in case of web apps) code and hide this from back-end (model) code. Models should be written so that they could be reused in a desktop or mobile app for example without issue. A model would know to check auth or authorization, but would not know that the username and password were coming in through HTTP forms. The controller is the thin layer that hides that. IMO, the controller is not reusable between frontends. A web user controller would need to be rewritten for a mobile app. The view and templates too, of course. Current technology makes it so that this is mostly a non-issue, as webapps (think: Rails, Django) are written in different languages and use different libraries from mobile apps (think: iOS apps, Android). But we must recognize this is an artefact of current technology that could change, and is not an ideal case. Anyone writing mobile apps in "web" scripting languages ? AFAIK, the iOS app store forbids non-Objective C apps. I could be wrong.
- Swannie 15y agoMy original answer was longer, but this is hopefully clearer. Small programs are often split: Input Parsing. Process. Output presentation. We can think of MVC where the View is the output presentation. The Model does the processing. The Controller does the initial input parsing, determining which part of the process to pass control to. The Controller as "dispatcher" or coordinator.
- dataduck 15y agoThanks, this helps! Can you recommend any further reading? Or is it simpler than I'm thinking?
- cks 15y agoInteresting reading but I don't get the rant about the controllers being so ugly containing the model logic? I mean it's essentially a matter where you place the logic. The logic itself whether in the controller or model will/could be very similar.
- mikeflynn 15y agoHe had me up until the part about models talking directly to views. I was nodding along until then. While I agree that the application business logic should be in models, but the controller needs to be that traffic cop, only asking for what the view needs and then handing it to the view. Having the view get this giant dump of things from models, skipping the controller entirely makes for confusing code as views can (and should) be reused for multiple data sets on different pages. The controller doesn't have to have much in it, but it's the traffic cop for the page (like a boss).
- rickmb 15y agoWhat you're suggesting is more like MVP (Model View Presenter). Making the view "dumb" in MVC simply results in a lot of overhead and duplicate logic inside the controller, that constantly has to "translate" stuff for the view. Having the view talk directly to the model actually makes the code a lot simpler (and a lot less of it). If all your view can get from the model is "a giant dump of things", there is probably something missing from the model. Because in that case, all the controller can get is also "a giant dump", which means you will need to add business logic to the controller to process this giant dump for the convenience of the view. Which puts us right back where we started: bloated controllers that contain business logic that should be in the model.
- mikeflynn 15y agoOk, fair enough on the "giant dump" statement. That wasn't phrased particularly well. What I was trying to say is that view should be 100% a reusable display, and as such it is given a chunk of data to display, instead of asking for it directly. Not every use case needs the same data, but the display code (say HTML structure for the web) should be able to be reused. Like a list that could be "top users" or could also be used on "top comments" (rough example). Maybe I'm off and what I'm describing is MVP rather than MVC, but the experience I described is usually the strict interpretation for MVC, whether we're talking about a PHP framework or an iOS app in Objective C. Edit/Update: ...however, there are times or projects that really need a modification to the "strict definition". For instance, a more modular approach works well at times, and thus you'd have to break some of the MVC/P rules. I did just that at work for a internal PHP framework and it's be a complete success, so obviously I'm not arguing you "shouldn't" or that it's "bad", but the author was discussing what he thought was the traditional definition of MVC, and that's where we disagree slightly in regard to models.
- rickmb 15y agoTo be honest, these problems are probably more endemic in the Zend Framework world than most other frameworks, since ZF was specifically designed to be a toolbox, not an opinionated framework, and therefor doesn't come with a standard way of implementing models. Personally I like this because it make ZF usable in a wide range of projects, but I've seen a lot of developers struggle with the concept of having to design the model themselves instead of filling in the blanks in the framework. Nine out of ten times they end up stuffing the business logic inside the controllers because "there's nowhere else to put it".
- vdm 15y agoThat's actually not a bad way of thinking about what frameworks are: a structure of places to put things.
- Swannie 15y agoHe still didn't clear up the conceptual understanding of "model" very well. The Data Model. Just the data. - pure Value Objects, classes which are little more than wrappers to data, which can be assembled in more complex structures for presentation by a view. The Domain Model. A representation of the data, the actors, and the processes. The objects are often a mix of pure value objects, object managers, actors that carry out actions on multiple data items, and facades/processes that coordinate several actors, maintain their own state (internal model/statemachine or externally in other objects). The "Framework Supported" Model (for want of a better term). This is where the data model has some aspects of the domain model, but a number of pieces are defined outside of the "model" and in a framework. Bits that are often separated are things like: validation, keeping track of sessions (actor state), collecting multiple models together for presentation to a view (maybe typically done in a facade), dependency injection, lazy loading, etc. Personally, if a framework has clean and clear support for things that I'd usually put in a Domain Model, I'm probably going to use it. Especially for input validation!! They usually end up in the "controller" for lack of a better place to put them. However, sometimes it makes sense to pull it into your model... especially where you may have multiple controllers/processes acting on the same data - and leaving it to the framework controller is going to be an issue. I'm not a fan of "views" pushing data back to the models without the controller... unless you're doing your validation and access controls, etc. in the domain model then it's a slippery slope. That said... if you are tracking state in the model, then the view updating state? That sounds OK... there are no hard and fast rules!