Restructuring a large ASP.Net Mvc app

So the issue is, you have a very large ASP.Net MVC app with many controllers and actions and don't know (or do) on how to restructure it for better maintenance.

This article contains some thoughts around what my experiences were with building a large scale app with 10K+ lines of jQuery and 266 Actions dispersed over 98 Controllers.

In the beginning:

Yes God created .... no I created a relatively small scale Mvc app based on a very limited set of requirements, more like a XP or Agile experience or learn as you go. The user community did not know what they wanted and the prototype was based on a missed conception of what the business' core day-to-day focus was (of course we did not know this at the time). There were only a few controllers and they were small.

Then we discovered the jQuery Grid by Tony Tomov (awesome free plug-in) which supports a ton of ajax features, so we decided to use it for all of our lists and also for a lot of CRUD functions. This very quickly expanded the controllers to a very large size. At this time I decided to not split the controllers as it might introduce bugs. For example an Account has a New, Update, Delete, Create and Edit action, and now it has a List and CUD action (2 actions) added for each grid. Since the Account page has 9 tabs with 13 grids it now has an added 26 actions on that single AccountController controller and then some for other ajax type functions.

So then very quickly, as the customers saw the app, new requirements were added and the controllers grew even bigger.

The decision then came as to what to do and how to refactor the code. I ran this past a colleague of mine to find out what he thinks: Large controller with all the logically related actions together (yes all 39 actions at some point) or to split them into smaller but many different controllers? It seemed that keeping it all in one controller was a good decision since when you need to fix something on the account page with all the tabs with grids on them you know you need to go to the AccountController controller, right!? However, what if the requirements change and these grids need to be also displayed in other places, that's right, like on the Contact's maintenance page that has the same tabs as the Account page (only filtered by the contact for that account). Now I have a duplicate set of actions that perform essentially an identical function to those on the Account's controller. That is when it became clear that the controllers' actions should not be arranged according to how the screens look like but to adhere to the SRP.

Lets look at an example. The Account has a list of Calls, Contacts, Contracts and Lessons Learned. Even though these are tabs on the account's page, they need to be self contained. So this is how they were disseminated and re-arranged to adhere to SRP:

AccountController: New, Create, Edit, Update, Delete, List actions

CallController: New, Create, Edit, Update, Delete, List actions

ContactController: New, Create, Edit, Update, Delete, List actions

ContractController: New, Create, Edit, Update, Delete, List actions

LLController: New, Create, Edit, Update, Delete, List actions

The AccountController had a very large constructor since we used IoC Castle Windsor to inject the Repository (DAO type functions) into the constructor; to support all the DAO functions for calls, contacts, contracts and lessons learned. Now the constructor of each controller is very small and only cares about the Repositories it needs. In addition, to mention one example, the Calls tab on the account and contact screens now share the same action defined on another controller specifically to support the jqGrid functions for calls, the CallGridController with its 2 (yes only 2) actions ListCallsForjqGrid() and SaveCallForjqGrid(), rather than duplicating it. Now another developer knows that no matter where the jqGrid is displayed for calls, they can always find it on the CallGridController.

These are just some of the benefits of creating smaller controllers. Feel free to comment and share your ideas and experiences.

Print | posted @ Wednesday, December 30, 2009 9:33 AM

Comments on this entry:

Gravatar # re: Restructuring a large ASP.Net Mvc app
by James Fleming at 1/8/2010 9:05 PM

I've got a big honkin MVC site of my own, which I've been refactoring. I agree it is best to make the controllers small. Without seeing exactly what you're trying to do, I'd suggest using ViewModels to manage some of the mapping for various screen rendering, also MVC RC2 now supports ActionResults, so you can do component based UI calls.

The real downside to one big honkin controller, is you wind up with lots of files, so organizing your directories & namespaces becomes more important.
Gravatar # Restructuring a large ASP.Net Mvc app
by Renso at 1/11/2010 8:41 AM

Thanks for the feedback James. Yes I prefer the smaller controllers and have had to give the folder structure extra thought. For example my CallController (a call in the sense of a customer service rep capturing a call's information to a client), has many factors to it, so as an example, you can plan a call, complete a call, schedule a call, and so on. I use a jQuery plugin to support lists (with CRUD support) and created controllers just for that so I ended up with:

Controllers/Call/CallPlan - CallPlanController
Controllers/Call/CallPlan - CallPlanGridController
Controllers/Call/CallPlan - ConfirmController
Controllers/Call/CallSummary - CallSummaryCOntroller
Controllers/Call/ScheduleCall - ScheduleCallController
Controllers/Call/ScheduleCall - ScheduleGridController

I poked other developers that have no idea of what my app looks like and asked them some questions about where they would start looking for issues/bugs and it became very clear that the name of the controller and then the action is key. One developer is considered a main contributor to unit testing in the community and places a lot of emphasis on naming, so for example except for the run-of-the-mill action names like, Edit, Create, Update, List, etc, some of my other action names are very long to create a clear understanding of what they do. Foe example some ajax actions needed to support some JavaScript calls look something like:

GetAllCallsWhereTheCallIsAssignedToGivenUserJsonFormatted(int userId, ...)

I added "JsonFomratted" to the name in cases where a create a JSON serialized string although the result is ActionResult, to help another developer understand it is not rturing a view or redirecting but returing a very specific result. This helps so that when a person is looking to re-use an action you do not have to drill down into every action to see if it returns a view, redirecting, or in this example returning a JSON serialized DTO. Of course you could do "public JsonResult MyAction()", but we use refactoring to auto setup security and look for public methods with ActionResult as the return type to distinguish them from helper methods that are not actual Actions (sure we have the [NonAction] attr on them too but I did not build the refactoring piece and if I had control over it could have simply filtered all public methods without the [NonAction] attr on them :-), just as an example of a benefit of always using ActionResult return type.

Thanks James.
Post A Comment
Title:
Name:
Email:
Comment:
Verification: