Rails Model, View, Controller, and Helper: what goes where? - ruby-on-rails

In Ruby on Rails Development (or MVC in general), what quick rule should I follow as to where to put logic.
Please answer in the affirmative - With Do put this here, rather than Don't put that there.

MVC
Controller: Put code here that has to do with working out what a user wants, and deciding what to give them, working out whether they are logged in, whether they should see certain data, etc. In the end, the controller looks at requests and works out what data (Models) to show and what Views to render. If you are in doubt about whether code should go in the controller, then it probably shouldn't. Keep your controllers skinny.
View: The view should only contain the minimum code to display your data (Model), it shouldn't do lots of processing or calculating, it should be displaying data calculated (or summarized) by the Model, or generated from the Controller. If your View really needs to do processing that can't be done by the Model or Controller, put the code in a Helper. Lots of Ruby code in a View makes the pages markup hard to read.
Model: Your model should be where all your code that relates to your data (the entities that make up your site e.g. Users, Post, Accounts, Friends etc.) lives. If code needs to save, update or summarise data related to your entities, put it here. It will be re-usable across your Views and Controllers.

To add to pauliephonic's answer:
Helper: functions to make creating the view easier. For example, if you're always iterating over a list of widgets to display their price, put it into a helper (along with a partial for the actual display). Or if you have a piece of RJS that you don't want cluttering up the view, put it into a helper.

The MVC pattern is really only concerned with UI and nothing else. You shouldn't put any complex business logic in the controller as it controls the view but not the logic. The Controller should concern itself with selecting the proper view and delegate more complex stuff to the domain model (Model) or the business layer.
Domain Driven Design has a concept of Services which is a place you stick logic which needs to orchestrate a number of various types of objects which generally means logic which doesn't naturally belong on a Model class.
I generally think of the Service layer as the API of my applications. My Services layers usually map pretty closely to the requirements of the application I'm creating thus the Service layer acts as a simplification of the more complex interactions found in the lower levels of my app, i.e. you could accomplish the same goal bypassing the Service layers but you'd have to pull a lot more levers to make it work.
Note that I'm not talking about Rails here I'm talking about a general architectural style which addresses your particular problem.

Perfect explanations here already, one very simple sentence as conclusion and easy to remember:
We need SMART Models, THIN Controllers, and DUMB Views.
http://c2.com/cgi/wiki?ModelViewController

The Rails way is to have skinny controllers and fat models.

Do put stuff related to authorization/access control in the controller.
Models are all about your data. Validation, Relationships, CRUD, Business Logic
Views are about showing your data. Display and getting input only.
Controllers are about controlling what data goes from your model to your view (and which view) and from your view to your model. Controllers can also exist without models.
I like to think of the controller as a security guard/receptionist who directs you the customer(request) to the appropriate counter where you ask a teller (view) a question. The teller (view) then goes and gets the answer from a manager (model), who you never see. You the request then go back to the security guard/receptionist (controller) and wait until you are directed to go another teller (view) who tells you the answer the manager (model) told them in response to the other teller's (view) question.
Likewise if you want to tell the teller (view) something then largely the same thing happens except the second teller will tell you whether the manager accepted your information. It is also possible that the security guard/receptionist (controller) may have told you to take a hike since you were not authorized to tell the manager that information.
So to extend the metaphor, in my stereotyped and unrealistic world, tellers (views) are pretty but empty-headed and often believe anything you tell them, security guard/receptionists are minimally polite but are not very knowledgeable but they know where people should and shouldn't go and managers are really ugly and mean but know everything and can tell what is true and what isn't.

One thing that helps separate properly is avoiding the "pass local variables from controller to view" anti-pattern. Instead of this:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
def show
#foo = Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= #foo.bar %>
...
Try moving it to a getter that is available as a helper method:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
helper_method :foo
def show
end
protected
def foo
#foo ||= Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= foo.bar %>
...
This makes it easier to modify what gets put in "#foo" and how it is used. It increases separation between controller and view without making them any more complicated.

Well, it sort of depends upon what the logic has to deal with...
Often, it makes sense to push more things into your models, leaving controllers small. This ensures that this logic can easily be used from anywhere you need to access the data that your model represents. Views should contain almost no logic. So really, in general, you should strive to make it so that you Don't Repeat Yourself.
Also, a quick bit of google reveals a few more concrete examples of what goes where.
Model: validation requirements, data relationships, create methods, update methods, destroy methods, find methods (note that you should have not only the generic versions of these methods, but if there is something you are doing a lot, like finding people with red hair by last name, then you should extract that logic so that all you have to do is call the find_redH_by_name("smith") or something like that)
View: This should be all about formatting of data, not the processing of data.
Controller: This is where data processing goes. From the internet: "The controller’s purpose is to respond to the action requested by the user, take any parameters the user has set, process the data, interact with the model, and then pass the requested data, in final form, off to the view."
Hope that helps.

In simple terms, generally,
Models will have all the codes related to table(s), their simple or complex relationships (think them as sql queries involving multiple tables), manipulation of the data/variables to arrive at a result using the business logic.
Controllers will have code/pointers towards the relevant models for the job requested.
Views will accept the user input/interaction and display the resultant response.
Any major deviation from these will put unwanted strain on that part and the overall application performance may get impacted.

Testing, Testing ...
Put as much logic as possible in the model and then you will be able to test it properly. Unit tests test the data and the way it is formed by testing the model, and functional tests test the way it is routed or controlled by testing the controllers, so it follows that you can't test the integrity of the data unless it is in the model.
j

Related

Rails MVC - Should database search logic go in the model or the controller

I would like to make sure my code is properly organized/designed according to the following paradigms/patterns:
- Model View Controller
- Rails Convention over Configuration
- Skinny Controller, Fat Model
I have listed them here in the order I think is most important.
My issue
After reading several articles, in particular this one and this one too, I started to move some of the logic in my controllers, to my models.
However, I can't decide whether or not to move my searching logic (explained in depth, later) from the controller to the model, even after reading more posts/articles like these: MVC Thinking, and Model vs. Controller, Separating concerns and many more not listed here.
The scenario
View
Two pages:
1 page contains a text field and submit button which sends the user's input as an argument in params in a POST request to the second page.
The second page simply renders each neatObject in a given array, let's call it #coolList.
Controller
A method, let's call it awesomeSearch, is invoked when the second page receives a POST request. (Thanks to Rails Routing)
awesomeSearch should take the user's input, available as params[:searchString], and work with the NeatObject model to build the #coolList and make that list available for the view to render.
Model
The NeatObject model handles requests from controllers and returns neatObjects back to those controllers.
The NeatObject model defines the relationship between neatObjects and other tables in our database.
Database
These are the attributes that make up each neatObject according to our database:
id - int
description - string
address - string
date_created - timestamp
What's Missing?
How the controller works with the model to get matches for the user's input.
This is the part I am confused about. The logic itself is very simple, but I am not sure which pieces belong in the model, and which pieces belong in the controller.
Should the controller pass the search string to the model, and the model pass back the results?
Should the controller ask the model for all of the neatObjects, then only keep the ones that match?
Is the solution a little of both?
To be able to ask questions about specific bits of the logic, I'll next outline the search process with more detail.
The search logic in depth
The process involves finding neatObjects that match the search string. It would be impossible to move on without defining what we consider matches for neatObjects. To keep things very simple, we will say that:
A neatObject matches a search string if the search string is contained within its description or its address, ignoring case and leading/trailing whitespace.
This definition is not guaranteed to be permanent. There are several things that may change our definition. We may need to test against more attributes than just address and description, perhaps if the database guys add a new important attribute, or the UI guys decide users should be able to search by ID. And of course, the opposite of these scenarios would mean we need to remove an attribute from the list of attributes we are testing. There are many situations that could change our definition of a match. We could even have to add or remove logic, perhaps if it is decided that we should only test the first word in the description attribute, or perhaps if we should no longer ignore case.
Now we know what defines a match and we know that our definition may change. Now we can more concretely define the search process.
Here is an outline of the steps:
Get a reference to all neatObjects
Loop through each neatObject, putting each individual one through the match test
Test passes - add/keep neatObject in results list
Test fails - do not keep neatObject for results
Make results available to the view
I will reference these steps as I show possible implementations.
Implementation
The search functionality can easily be implemented in either the NeatObject model, or the controller serving the view.
Normally, I would just write all of the logic in the controller, but after I learned about the "Skinny controller, Fat model" design, I thought it definitely applied in this situation. This article in particular made me think to reorganize my code, after I saw the author had implemented a "search-like" function in the model. The author's function did not handle user input though, so I was wondering how it should be handled.
This is how I would have written the code before learning about "SCFM":
Search logic in controller:
#searches_controller.rb
#This is the method invoked when second page receives POST request
def search
#neatObjects = NeatObjects.all.to_a
#neatObjects.delete_if {
|neatObject| !matches?(neatObject, params[:searchString])
}
end
def matches?(neatObject, searchString)
if((neatObject.description.downcase.include? searchString.downcase) ||
(neatObject.address.downcase.include? searchString.downcase))
return true
end
return false
end
This method gets its reference to all of the neatObjects (Step 1) by calling .all() on the NeatObject model. It uses the array function delete_if to perform the match test on each neatObject, and only keeps those that pass (Step 2). Step 3 is accomplished automatically automatically since we store our results in an instance variable in the controller which serves the view.
Placing the logic in the controller is very straight-forward, but when considering the "SCFM" design pattern, it seems very illogical.
I've written another option, in which the controller sends the user's input to a function in the model, which will return the the neatObjects which match the input.
Search logic in Model
#NeatObject.rb
def self.get_matches_for(searchString)
all.to_a.delete_if { |neighborhood| !matches?(searchString, neighborhood) }
end
def self.matches?(phrase, neighborhood)
fields = [neighborhood.name, neighborhood.address]
fields.map!(&:downcase)
phrase.downcase!
fields.each do |field|
if (
(phrase.include? field) ||
(field.include? phrase)
)
return true
end
end
return false
end
This method gets the complete list of neatObjects with all() (Step 1). Just like the first method, the model method uses delete_if to remove array elements (neatObjects) that don't meet a certain criteria (passing the match test) (Step 2). In this method, the controller serving the view would call get_matches_for on the NeatObject model, and store the results in an instance variable (Step 3), like this: #neatObjects = NeatObject.get_matches_for( params[:searchString] )
I do think that the model option is cleaner, and slightly more maintanable, but I'll go more in depth in the following section.
Concerns
I can see pros and cons to both the model method and the controller method, but there are things that I'm still unsure about.
When I read over that article I've referenced several times (just like I did here), it was very logical that the model defined a function to return the recently added people.
The controller didn't have to implement the logic to determine if a person had been recently added. It makes sense that the controller shouldn't, because that is dependent on the data itself. There might be a completely different implementation of the "recency" test for messages. The recent people might include people added this week, while recent messages are only those messages sent today.
A controller should just be able to say People.find_recent or Message.find_recent and know it got the correct results.
Is it correct to say the find_recent method could also be modified to take in a time symbol, and return objects for different time periods? Ex - People.find_in_time( :before_this_month ) or Messages.find_in_time( :last_year ). Does that still follow the MVC pattern and Rails convention?
Should a controller be able to find matches for user input with NeatObject.get_matches_for( searchString )?'
I think the matching logic belongs in the model, because there are certain/specific attributes that are used in testing, and those attributes are different depending on the data. We might have different attributes for different tables, and those attributes definitely shouldn't be defined by the controller. I know the controller depends on the model, not the other way around, so the model must define those attributes, even if the rest of the logic is in the controller.
And if the model defines the attributes that are used when searching, why shouldn't it define the entire search function?
The above text explains why I think the model should handle the searching logic and expresses the majority of my questions/concerns, however I do have some opinions favoring the other option.
If the model is concerned only with the data, how can one justify passing user input to it?
If the controller doesn't handle the search logic, it still needs to send the user's input to the model. Does it clean it up before it sends it? (Removing leading/trailing whitespace and downcasing the string.) Does it just send the exact input it got from the user?
Where is the testing done to make sure the input is not malicious?
Some of my biggest questions:
Does the answer for where the logic goes change for each case?
If the search/matching process was simpler, would the code's place change? For example, if the searching was simpler:
If the only attribute being tested was the address and that was unlikely to change, would we just handle the search with the controller?
If we were making an advanced search feature, where the user decided which attributes to include in the search and also controlled some other factors, there would be a lot of user input just to define the arguments to the search function. Would that be too much logic or user input to place in the model?
In conclusion
How can I always be sure that I am putting logic in the right place (MVC)?
For my specific case, with this data and this search/match process, how should I organize the code?
What guidelines can be followed when you find a gray area, or are unsure about where logic belongs?
This is something that varies a lot between cases (each application is different) but here's what I do on my end (large sports app that searches lots of tables from many different places):
start by performing small searches with non-repeating code patterns in the controller (or rarely view)
when it turns out the code is needed in more than one or two action/view I move it to the models
I also move the code to the model when the query complexity goes above the average .find or simple .where
Like this, you don't spam your model with one-time-use methods, and at the same time don't repeat the same code over multiple controllers/actions/views
As many thinks in IT and in life, It depends of the scale and the goal.
Considerations:
1) For design: If you see you are violating DRY in your controllers many times, its probably time to move your logic to models.
2) For performance: Since controllers are loaded more than models, Logic in controller tend to performs worst.
And not less important: Unless you are doing something very trivial, and you db has a few thousands rows, do NOT use db for text search. Instead, use a search engine like Solr, ElasticSearch, Sphinx, etc.

Is that a proper way to refactor ActiveRecord fat models?

If for example I've this ActiveRecord model:
app/models/order.rb
class Order < ActiveRecord::Base
# model logic
end
require "lib/someclass.rb"
lib/somelass.rb
class Order
before_save :something
# more logic here
end
Is that a good way to refactor/extract logic from model?
Or maybe use concern class, service class or something else?
Like someone told me a long time ago:
Code refactoring is not a matter of randomly moving code around.
In your example that is exactly what you are doing: moving code into another file
Why is it bad?
By moving code around like this, you are making your original class more complicated since the logic is randomly split into several other classes. Of course it looks better, less code in one file is visually better but that's all.
Prefer composition to inheritance. Using mixins like this is asking to "cleaning" a messy room by dumping the clutter into six separate junk drawers and slamming them shut. Sure, it looks cleaner at the surface, but the junk drawers actually make it harder to identify and implement the decompositions and extractions necessary to clarify the domain model.
What should I do then?
You should ask yourself:
Which code goes together and could be part of a new class / module ?
Where does it makes sense to extract code to somewhere else ?
Do I have some piece of code that is shared across my application ?
Can I extract recurrent patterns in my code base ?
Extract Service Object
I reach for Service Objects when an action meets one or more of these criteria:
The action is complex
The action reaches across multiple models
The action interacts with an external service
The action is not a core concern of the underlying model
There are multiple ways of performing the action
Extract Form Objects
When multiple model can be updated by a a single form submission, you might want to create a Form Object.
This enable to put all the form logic (name conventions, validations and so on) into one place.
Extract Query Objects
You should extract complex SQL/NoSQL queries into their own class. Each Query Object is responsible for returning a result set based on the criterias / business rules.
Extract Presenters / Decorators
Extract views logic into presenters. Your model should not deal with specific views logic. Moreover, it will enable you to use your presenter in multiple views.
More on decorators
Thanks to this blog post to help me putting these together.
Keeping the code in the same class moves logic, it doesn't extract it.
Externalizing callback declaration is misleading and potentially dangerous. Callbacks are abused enough; forcing readers to hunt down related files is cruel.
There's no general way to answer the question as asked; the "best" refactors depends on what's actually being refactoried. Lifecycle information should be obvious and precise, though.
Concerns, services, decorators, facades, etc. are good mechanisms that support refactoring. Without knowing what's being refactored it's impossible to provide meaningful advice regarding what's "best".

In MVC3, should I have separate "edit" models vs. "display" models?

With MVC3, should I design my view models such that there is one that is bound to the view (DisplayModel), and one that is posted back to the controller (EditModel)?
To clarify, I am not asking about data models vs. view models -- I know it's not good to bind my views/controllers to data/domain models.
Nor am I asking about sharing one model across two separate views, one view that is used for displaying the data, and another view that is used for editing the data.
Rather, I am asking about one view that is used for editing data, and the model that is bound to the view vs. the model that is bound to the controller action.
In other words, if this is my view:
#model MyApp.Models.CustomerModel
Should my controller action look like:
public ActionResult Index(CustomerModel model)
Or:
public ActionResult Index(CustomerEditModel model)
At one point, we were doing the latter (separate). But lately, we've started doing the former (shared).
The reason for this change was because:
With MVC3 unobtrusive validation, if I'm using DataAnnotations on my model for validation, this is needed in both models if they are separated (on the display model to map client-side validation, and on the edit model for server-side validation).
As our application matured, we realized that our display and edit models were 95% identical, with the exception of the select lists that were in our view models. We've now moved these to a shared class and are passing these in via the view now.
But I've seen some other discussions that point to having shared models for view/controller to be a bad idea, and that it violates separation of concerns.
Can someone help me understand the tradeoffs for these two approaches?
I've seen perfectly good arguments for and against, it just depends what works best for your application. There's no one size fits all approach that can be applied!
If you haven't read it Jimmy Bogard has written a very good post about how his team does MVC here, which covers this topic.
I agree with rich.okelly's answer that there's no right approach.
There are a couple of concerns I have with using one model, though.
It's going to be very to always use one model without having unneeded properties when the view needs to display a selectable list of objects. The model will need to have the list of objects as well as a property to accept the POSTed value the user chooses. These unneeded properties add a small amount of code clutter and overhead.
(One way around this is to have the model contain only selected ID and have HTML helpers to build the lists.)
Another concern is more related to security.
A common scenario is displaying information in a form that should be considered read-only.
In the case of a ViewModel and an EditModel, the EditModel will only contain properties that are expected to be POSTed, whereas the ViewModel will contain all of the properties.
For example, if a form displays a user's salary, a user will be able to POST a 'salary' and have it bound to the ViewModel's Salary property automatically by MVC.
At this point, something has to be done to ensure it doesn't end up in the database. It could be if/else logic, a Bind attribute, Automapper logic or something else, but the point is that it's a step that could be overlooked.
When considering the lifespan of an application, I like the explicitness of the EditModel over time.
These concerns don't mean that two models are good and one model is bad, but they should be considered when choosing a design.
If the properties are the same for display and edit view models I see no reason to have separate classes.
I think you'll find that it's hit or miss no matter what way you go but if you can take the path of easiest maintainability then you should do that. In my experience, having a single model is much easier to maintain, obviously, but it seems that there is always some business decision that is made that forces me to split the models. If you're in that 95% then I think you are in really good shape. Your application, from a maintainability perspective related to your models, will be easy to maintain. When a change comes along, you have one place to make that change, for the most part. The issue I always seem to run into is scaling business changes across multiple models. Copy/paste issues, or simply forgetting about some property somewhere, always seems to hurt me because of the multi-model issue.
we realized that our display and edit models were 95% identical, with the
exception of the select lists that were in our view models. We've now
moved these to a shared class and are passing these in via the view now.
Are they 95% identical in data and operations or only in data? Remember that classes encapsulate data and behavior.
If they are 95% similar in properties but have totally different operations you might benefit from splitting them in two classes. Or you might not :)
As others pointed out there is no one-size-fit-all answer and in your case it seems that one class is OK...but if you start noticing that the behavior on each of them is unrelated don't be afraid to rethink you approach.
No - one view model for both directions. Mixing it up is not only harder to follow, but one could easily inject invalid values into the page that then get automatically bound. I could overwrite your customerid (or create one) for example.
Inherit from a base view model if you must or don't rely on data annotations at all and use the fluent api on your model save.
A great link (somewhat unrelated but the auto map is nice)
edit
(sorry someone else previously posted this below I just realized)
http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
Also
ASP.net MVC - One ViewModel per View or per Action?
You (IMHO) should be generally binding to your method specific VieWModel rather than a shared view model. You could get caught in a trap of missing properties, etc. but it may also work just fine for you.
Use auto mapper to go between both. Jimmy also has a nice AutoMap attribute when returning to the View. Going back the other way I would not use a CustomerModel in general as there may be fields required in there that are not coming from my say, create view. For example a customer id may be a required field and for a "create" action it won't be present. But - if you find in the most of your cases this to actually work for you, then there is no reason at all not to use it.

Is it considered bad practice to write db queries in a controller rather than a model

I've read that best practice dictates fats models, skinny controllers.
Models should contain business logic such as getting a list of customers based on parameters sent from a controller.
Controllers should contain just enough logic to invoke methods within a model to return to a view.
However I see many examples and tutorials where there is logic within the controller such as a query that accesses a db to get a list of products. I was under the impression that the logic should live in a method inside a model. The controller can then invoke this method, rather than containing the actual logic to query the database.
So if I have a ProductController I might have a Index action which returns an Index View, and I would have a ProductModel which would house my logic to display certain products based on a query string(which the ProductController would pass to said model). Right?
So if I have a ProductController I might have a Index action which returns an Index View, and I would have a ProductModel which would house my logic to display certain products based on a query string(which the ProductController would pass to said model). Right?
That is correct. As per the Model-view-controller architecture:
The model manages the behavior and data of the application domain,
responds to requests for information about its state (usually from the
view), and responds to instructions to change state (usually from the
controller). In event-driven systems, the model notifies observers
(usually views) when the information changes so that they can react.
The view renders the model into a form suitable for interaction,
typically a user interface element. Multiple views can exist for a
single model for different purposes. A viewport typically has a one to
one correspondence with a display surface and knows how to render to
it.
The controller receives user input and initiates a response by making
calls on model objects. A controller accepts input from the user and
instructs the model and viewport to perform actions based on that
input.
Keep the data-related queries and operations in the model; stuff as much as you can in there in accordance with DRY (Don't Repeat Yourself). Make the functions reusable as much as possible so they can be ported into various controllers and used throughout the application as necessary.
The view should contain little - if any - logic outside of view-specific work.
Your controller functions should invoke the model functions required to retrieve and manipulate data, and should be as "thin" as possible (as you pointed out). The smaller and less involved the controller, the easier it will be to add asynchronous features that "don't reboot the application" on the front-end, making for a better user experience. (If you are concerned about this, anyway!)
The Add Controller dialog box in VS 2010 has the option of adding a scaffold with CRUD functions into the controller. This should tell you quite a bit above how the ASP.NET MVC dev. team views this debate.

God Controllers - How to prevent them?

In a few MVC projects I've been working on, it has become apparent that there are a few problematic controllers that have organically grown into God classes - demi-gods each in their own domain, if you will.
This question might be more of a matter 'what goes where,' but I think it's an important question with regards to SRP (Single Responsibility Principle), DRY (Don't Repeat Yourself), and keeping things concise, "agile" -- and I am not experienced enough (with this pattern and in general design) to be knowledgeable about this.
In one project, we have a NutritionController. Over time it's grown to include these actions (many with their respective, GET, POST, and DELETE methods):
Index (home controller)
ViewFoodItem
AddFoodItem
EditFoodItem
DeleteFoodItem
ViewNutritionSummary
SearchFoodItem
AddToFavorites
RemoveFromFavorites
ViewFavorites
Then we have an ExerciseController, which will include many similar actions, such as the searches, and favorites actions. Should these be refactored into their own controller so that it's something like so?
SearchController {
SearchExercise
SearchNutrition
//... etc
}
FavoritesController {
ViewNutritionFavorites
AddToNutritionFavorites
AddToExerciseFavorites
EditNutritionFavorites
EditExerciseFavorites
//... etc
}
It just seems to me that if you break them out into separate controllers, you're going to grow an unbelievably large dependency at some level to deal with the information that you will need. OR you are going to have a completely generic handling application that will be very difficult to handle since you will have to jump through so many hoops to get your desired effect (either at the M, V, or C level).
I am thinking about this the wrong way? For example, should I have a generic Favorites object and then let the controller decide what view to throw it to?
*Sorry for spelling out the acronyms -- I'm doing so in case anyone else comes across this question and is clueless as to what those things are
EDIT:
All the logic I perform is pretty much handled in the service layers. For example, the controller will send the 'new' FoodItem to the service. If it already exists, or there's an error with it, the service will bubble it back up to the controller.
I would break your first list up based on responsibility:
HomeController
Index
FoodItemController
ViewFoodItem
AddFoodItem
EditFoodItem
DeleteFoodItem
SearchFoodItem
NutritionController
ViewNutritionSummary
FavoritesController
AddToFavorites
RemoveFromFavorites
ViewFavorites
SearchFavorites
Django's approach to MVC is to separate responsibilities into "applications", each with their own models, controllers, and even templates if necessary. You'd have a Food app, a Nutrition app, a Search app, and a Favorites app, most likely.
Edit: The OP mentioned that searching is more specific to each controller, so I've made those actions. However, searching may also be just a general global thing, so in those cases, a SearchController would be fine.
Do as Soviut says. You want to keep the controllers simple. It sounds like your ending up with too much coordination logic in your controllers. Remember they are responsible for hooking up a view and a model. This coordination logic should probably be split out into services.
I get this feeling because you mention the possibility of your controller growing huge dependencies. Well If FavoritesController needs to know about nutrition AND exercise favorites (to display in the same view) don't make your controller dependent on 2 repository like classes. Instead, encapsulate that coordination behavior. Maybe create a FavoritesService that knows how to return both nutrition and exercise favorites. That service might delegate to NutritionFavoritesService and ExerciseFavoritesService. This way you're controller only ends up with 1 dependency, you're keeping things DRY, enforcing the SRP, and concentrating your business logic in some place other than the controller.
I'm not too familiar with that framework, but I can offer a bit of general advice. A controller should probably only know how to complete a single action, or to invoke other, single action controllers to complete a sequence of related actions. Any information that must be passed around from action to action should probably be somehow passed through the model layer, since that information is most likely relevant to the underlying model.
I have also experienced these sorts of maintenance headaches and find sticking to a "Rails" like method to be very helpful in keeping my controllers focused and unbloated.
If I find myself adding actions with unusual names Eg. To use a blogging example, AddPostToBlog, that would be a flag to create a new Post controller with a Create action.
In other words, if the action is not either one of the Index, New, Create, Show, Edit, Update and Destroy actions, then I add a new controller specific to the action I require.
For your example.
SearchController {
SearchExercise
SearchNutrition
//... etc
}
I would refactor this to...
SearchExerciseController {
Index
}
SearchNutritionController {
Index
}
This may mean having more controllers, but in my opinion thats easier to manage than ever expanding "god" controllers. It also means that the controllers are more self documenting.
Eg. Does the SearchExercise action, return the View to search the exercise or does it actually perform the search? You could probably ascertain this by looking at parameters and body, but its not as easy as for example a New and Create, or Edit and Update action pair.
SearchController {
SearchExercise
}

Resources