@DavinTyon's answer was very good, but I think that there are a couple other things worth mentioning. For better or for worse, the router/controller part of MVC is functional. The hierarchy works like this:
- The router takes the URL and figures out which function it should call
- The controller acts as a module to encapsulate related functions
- The action is the specific function that gets called
This fits well with mapping functionality to URI space. One thing that search engines have taught us is that URIs matter. They affect our page ranking, and they are contracts to consumers (users or machines) that you can get a resource at this location.
The specific action is a set of instructions on how to prepare the resource for the request. Mixing functional and object oriented approaches feels disjointed and difficult to wrap your head around in many cases. Unfortunately, as we build more and more distributed systems, the influence of functional programming will only get greater.
MVC attempts to help bridge that divide:
- Controllers are modules of related actions (functions)
- Actions prepare resources for the view templates
- Models are object oriented and are where you apply all your object oriented principles.
I think this is a better way of wrapping our head around an MVC application, and also helps us find the right patterns and design principles for the various pieces.
One of the main drivers behind MVC was also improving test-ability. You can run the actions on a controller without a web server or full infrastructure available. You can also test the contents of the Model and ViewBag that were passed into the View object returnd from an action. The model remains as testable as you designed it. The last piece of making sure the view looks right remains a manual process, but the riskiest portions of the app can be fully tested.