Wednesday, December 30, 2009

Restructuring 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 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.