It’s been over a month now since we did the final handover of a one and half year long project that, among other things, was built on top of ASP.NET MVC 1.0. Normally I do not like to use the term “best practice” since it does not communicate the context of which these practices are applied. Also, the “best”-part imply there is only a single solution that would effectively solve a problem in the best way. Instead I’d like to share a list of things that worked well for us and what to look out for. By the way, you can read more on the topic of best practices in Dan North’s great InfoQ article: better best practices.
Anyway, here is the first part in a series of posts with our experiences and what seemed to work well for us.
Do’s and don’ts
Keep Controllers thin
Controllers that have too many dependencies, service too many requests and/or contain domain logic are bad, really bad. Needless to say this make code unnecessarily difficult to maintain. I have a theory that one of the reasons why this happens is that controllers are on the top of the call stack and that controllers generally do not reference each other which making reuse harder. Lack of discipline is of course another plausible cause.
To mitigate this phenomenon you have to stand by your principles and push coordination between domain entities to domain level services. Adhere to the Single Responsibility Principle. Continuously organize controllers logically, preferable after user activity and not by domain entity that you see so often in demo code.
I’ve found this post by Ian Cooper to be an excellent resource on this topic.
Avoid Magic strings
This is without a doubt the biggest drawbacks with the framework. Magic strings are everywhere. If you are like me and want things to be strongly typed you need to do some extra work. For example, use the strong typed HtmlHelpers in MVCFutures. Other strategies can be to gather strings in static properties and classes, avoid FormCollection and so on. Hopefully, many of these things will be addressed in v2 of the framework.
Use dedicated ViewModels
Why? Because you will never be able to use your domain entities directly in views. Take the example of a User entity. In the view you may want the user to retype a password to make sure it’s correct. At the same time the domain entity has no need to represent this so what do you do? Also, a view to edit a product would not only need to pass the Product object as ViewData but also the categories that you can assign to it. So you have to create some aggregate structure to represent this. This post illustrate this very well.
Creating a ViewModel and copying values back and forth for each and every view sounds like much work. It is important however, to separate these different concerns. Fortunately there is an excellent tool called AutoMapper by Jimmy Bogard that helped us a great deal with this.
Avoid tag soup
In MVC WebForms you can’t use the normal server controls. If not careful you will end up with a mix of html and code that would remind seasoned Microsoft developers of ASP classic. Typically this is caused by introducing control logic and presentation logic into the .aspx file. Now, while there is nothing wrong with presentation logic in the view it makes the code so utterly hard to read.
One of the main reason why ASP.NET and WebForms replaced ASP classic was just to make this separation of logic and presentation and in a sense MVC undoes this. I would not worry too much over this though. Remember to put control code in HtmlHelpers extension and move conditional html into PartialViews. “If there is an if, write a HtmlHelper extension”. As an example we had an extension that would be used in this way:
Html.AsList<SimpleProductRowPartial,Product>(productsList);
The first generic argument is the type of the view (SimpleProductRowPartial) and the second the type of the ViewModel (Product) that would be passed to the view. The argument then is an IEnumerable of ViewModel. This method would then go ahead an render a ordered list (<ol>) with the content of list items set to whatever the view outputted.
Another tip is to move logic to JavaScript where it can be tested better. Or even better, consider not using the default web forms view engine and instead look into using an alternative like Spark.
Closing notes
Do not take examples from Microsoft evangelists or from the ASP.NET MVC team members as guidance. Or anyone else that is demoing a made up application, this includes myself. Often these are just samples to illustrate a certain feature of the framework and not how you go about to develop a 200 page website. The framework itself is not prescriptive, which is a good thing. Although, this forces to make carefully weighed decisions on which conventions and rules to follow. Hopefully this post helped you start thinking of a few of these decisions.
I will be posting part two of this series sometime next week that will contain even more of the do’s and don’ts .
-Anders