The M in MVC: Why Models are Misunderstood and Unappreciated(blog.astrumfutura.com) |
The M in MVC: Why Models are Misunderstood and Unappreciated(blog.astrumfutura.com) |
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.
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.
This fits both generic 'MVC' practice, AND Rails MVC (if you think they are different...)
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!
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:)
* 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).
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.
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".
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).
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.
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.
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)
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.
The point of MVC, as originally practised in Smalltalk and used by numerous GUI libraries ever since, is the three-way separation of concerns between the underlying data, the presentation of that data, and interactions that change that data. It's an elegant way to reduce dependencies, in particular eliminating dependencies from the model onto any part of the UI, by having some parts of the system push information and others pull it. It remains a useful basic architecture for interactive GUI applications to this day.
In the modern corruption used by various web frameworks, three subsystems have the same names, and that is about where the similarity ends. The three components are typically arranged in a linear stack, with linear dependencies. The responsibilities for the "controller" are very different.
This leads to comments like the parent post:
He 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.
If your view can't present data from your model without going via a controller, then you simply aren't using MVC. Your design might be perfectly reasonable and do its job well, but please call it something else.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.
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.
The term Model on it's own doesn't mean much, and is very overloaded with meaning. You can see it as the data objects (stuff like Person(id, firstName, lastName) for example), but that's one definition of it.
Not sure which pattern you're thinking of, but it's not MVC.
Basically, there is no difference between:
<?php echo $data['email']; ?>
And <?php echo $data->getEmail(); ?>
Both are asking for the email.Take your list code in HTML example. You can easily create a view that asks for data from a model, and simply ensure that the model always uses the same interface. Now, any model that should be listed has the same interface the view can access.
Views shouldn't be running actions on the model, of course. But it makes no sense to take data from a model, compile it up, and send it over to the view, when you can simply send the model to the view.
In Rails, :except and :only are super useful on filters.
Also, another possibility is to noun-ify some verbs. (Some may prefer to call it resource-ification, or REST-ification)
For example, you could create a PasswordResetRequest model, which handles all of the logic for checking the old password, the password confirmation, the new password's complexity, etc. Then it would be responsible for executing the actual password change as well.
As a bonus, noun-ification enables easy logging because you'll already have a data structure to store.
Here is a simple example for hooking in route-specific middlewares: https://gist.github.com/978411#file_express_middlewares.coff...
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 ...
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.
Then again, stuff like authentication and authorization (two separate concepts!) are often better handled on some middleware or service layer than in models and controllers.