ASP.NET MVC Design Philosophy

This week the first preview of the ASP.NET MVC framework was released to the web as part of the ASP.NET 3.5 Extensions CTP Preview. It's been a few months since we started coding and as the lead developer on the project I'd like to share my thoughts on the design of this framework. This isn't a post about why MVC is great. Instead, it's a post about what we did to make MVC happen in ASP.NET.

The MVC Snake

One of the design strategies we used in the MVC framework is to have a clear sequence of stages in each request. The stages are reminiscent of the ASP.NET Page and Controls lifecycle, but much, much simpler. Here's a rough diagram of the stages from the slides that Scott Hanselman and I showed at the DevConnections conference.

MVC_Snake

When a request comes in, it goes through the stages as shown in the diagram. There are a few more stages than are shown, but they were removed so the "MVC Snake" diagram wouldn't get too cluttered. An important aspect of this diagram is that each stage only depends on the previous stages. A given stage typically doesn't make any assumptions about what follows it.

For example, the IController interface doesn't assume anything about views or any particular view factory implementation. However, views can assume that an IController was involved in their creation. The implication of this is that you can stray from our path at almost any stage and still end up with success. Want to implement a controller in some new, unique way? Go ahead!

URL Routing - Not part of MVC

Part of the MVC framework is a new URL routing mechanism. This allows you to create "friendly" and "pretty" URLs such as "http://example.org/store/categories" that map to arbitrary controller actions. There's no reason that the URL should specify a physical resource. The visitors to your site surely don't care how you architected your web site or web application.

If you've downloaded the CTP you'll notice that the URL Routing types sit in the System.Web.Mvc namespace. However, the URL Routing types aren't even a part of the MVC framework. URL Routing works with arbitrary IHttpHandlers so it can serve up arbitrary requests - not just MVC requests. For the current CTP because we just couldn't find a better place so we left the types in that namespace.

New Language Features

We're technology addicts, and it shows. The latest version of the C# language includes several new features and we decided to take advantage of them in the MVC framework. We used anonymous types in place of dictionaries, extension methods for helper methods, and lambda expressions as strongly-typed URLs.

But - and there is almost always a "but" - we went a bit too far in the current CTP. We've heard from many of you already that using anonymous types as dictionaries is cool, but hard to understand. And it doesn't have Intellisense. For the next release we're still planning on keeping those features, but we want to add more familiar ways of doing the same things. For example, we're going to let you use dictionaries when we want dictionaries. It seems obvious now. :)

Extensible, Extensible, Extensible

To build a framework that was extensible, testable, and functional, we had many important design decisions to make.

Perhaps most importantly, we went with an interface-based design. We didn't use interfaces for everything, but we did for most things. Typically we used interfaces for all behavioral types, and classes for data objects. The reason is that behavior needs to be mocked, whereas data is just data. Your data objects would probably look exactly the same as our implementations anyway, so we didn't want to bother with interfaces. There are always exceptions, and in the first CTP we may have made some decisions that don't agree with this, but we already have plans to address these for the next CTP.

Traditionally in ASP.NET, a lot of extensibility is enabled by inheriting from our classes and overriding virtual members. While it is a valid approach, it requires that you inherit from our components even if you just want to tweak one small behavior. In the MVC framework we still have many instances of that, but we also allow you to extend our functionality using composition. For example, to modify the behavior of a controller you can either derive from it or you can use a filter to externally change its behavior. The same exception I mentioned in the previous paragraph applies here.

Composition often yields better separation of concerns and allows you to more easily build cross-cutting concerns. For example, if you want to apply the same "behavior modification" to several different controller classes, you don't have to override the same virtual members in each one. You can just apply an external cross-cutting concern to the controllers you want. Thus, composition is more easily used with dependency injection frameworks.

What Does All This Lead To?

Well, what does this all lead to? Several things, of course. For example, each of the components in the MVC framework is fairly small and self contained, with single responsibilities. This means that due to their small size you have building blocks that are easier to understand. It also means that you can replace or even alter the building blocks if they don't suit your needs.

Having great amounts of extensibility means you can write unit tests for your application more easily. I remember the days when I didn't write unit tests. Dark days, they were.

But who is going to use the MVC framework? Not everyone wants to write unit tests or alter so-called building blocks. Being able to drag & drop a GridView and a SqlDataSource onto a WebForm is a valid scenario and it will continue to work. We're trying to create a better fit for ASP.NET for certain types of individuals.

Anecdotes and Miscellany

And to end with some fun facts and stories, the following is all true.

  • For the released CTP, the unit test code coverage numbers were about 93%, far more than any other major feature area. This does not include the code coverage by our QA guys, which I'm sure would bring the number up to at least 99%.
  • We had about 250 unit tests for MVC, and the ratio of unit test code to product code is about 1.9 to 1 (in terms of the size of the code files, not lines of code; I'm too lazy to do the latter!).
  • It feels good to unit test! I can't go back to the old ways.
  • Some of the code is written using true TDD, but some is code-first, but it is then quickly followed up with unit tests (prior to checking in).
  • Several weeks ago we invited several customers for a top secret sneak preview of the CTP, including the MVC framework. Jeffrey Palermo was attending and trying to build some samples with the MVC framework. Sure enough, he encountered some bugs in the product. The bits were still hot off the press and some of the bits hadn't quite settled in the right place. Millions of ones and zeros - what are the odds they'd all line up correctly on the first try? Anyway, I sat with Jeffrey for about half an hour and we managed to use many of the extensibility points in the framework to work around the bugs he encountered. The whole time I kept thinking (and probably saying aloud) that I was so glad I made those methods public or virtual or whatever! Without them Jeffrey would have been stuck.
  • The moral of the story: If you aren't sure, make it public. If we have to use a method, chances are, someone else does too.

MVC Resources

Downloads:

Documentation and discussion forums:

Blog posts and videos by team members:

12 Comments

  • "The moral of the story: If you aren't sure, make it public."

    and Virtual??

  • Hi,

    I believe DLR (RTM) is coming by March/April end, along with Silverlight 2.0. I think we shall be getting the final version of MVC during that period only.

    IronRuby, IronPython and Silverlight 2.0 are all based on DLR.

    Can you blog a bit about the MVC support on DLR.
    This will really boost the moral of developers who are banking much on DLR and Dynamic Languages.

    Thanks

  • "The moral of the story: If you aren't sure, make it public."

    Could not agree more.

  • How are we supposed to save the state with MVC -- something like ViewState?

  • Eilon,

    Great example of a blog post, in form (great outline) and in premise (useful info). Also, hopefully, this type of "transparency effort" [allowed] by MS will be a trend.

    Well done,
    Troy

  • If the URL routing is handled by an HttpHandler, how does it work without having IIS map the wildcard to ASP.NET? /products/apples/2 wouldn't normally map to ASP.NET.

  • @Jeff - It works in IIS 7. For IIS 6, you need to use [controller].mvc and map .mvc to ASP.NET.

  • bingback from Shatl's space

  • Sooooo, when is the next CTP coming? :)

  • Eilon,

    I applaud your very "non-MS" like thinking. Just because you can't think of another use for your code, doesn't mean that somebody else won't be able to. Closing all access down with privates and internals is a sure way to frustrate your developers.

    Just the other day I had to rip out MimeMapping class from ASP.NET with Reflector... A perfectly good class, doesnt depend on anything... yet it's internal.

    I'm writing my own opensource API right now and I keep everything public and moreover all properties have a setter and 100% of the API is exposed through interfaces which all together means that any part can be swapped out for a custom implementation. Just because the user can break the functionality, doesn't mean they shouldn't be allowed to change it. It's a hard way to make C# API "Ruby-esque".

  • Great blog entry, very helpful.

    I have a question in regards to design. We are very heavy on an inheritance and web user control model here where I work. What do you think the best way woudl be to leverage the MVC model while still keeping our current model in place?

    Thank you in advance for your feedback. Have a great day!

    Paul >

  • Hi Eilon

    I referred to your blog last year when we were in the process of building our ASP .Net framework and have recently rediscovered your posts. The ongoing discussion about the MVC framework is fascinating and I can draw many parallels with our own work. In fact a lot of your recommendations we had already undertaken to produce our ASP .Net application.

    The framework we developed needs to be built upon and customised much like the ASP .Net framework so keeping everything simple and extendible was key to the delivery. As you have observed the simplest and best solutions are solved by programming basics such as favouring composition over awkward inheritance chains. With that in mind we contemplated building an object builder to bind (or inject) the instances of objects at runtime using a factory to generate the correct classes (yes I do know a framework such as Spring would do that). When we required specific control or behaviour we implemented the Template Method Pattern.

    However there are occasions where inheritance is best and rather than use interfaces we decided to produce a set of abstract classes as I felt it was less restrictive than interfaces. The issue with the interface is that (obviously) it is such a strongly binding contract. The use of abstracts was justified as our development was ongoing (after release) and we could alter or add methods, if it were an interface then it would have to be created as a V2 contract and the code modified.

    In the respect of our development the hardest aspect we had to overcome and keep under control was not exactly how things should be done - we were never short of idea's - but could the developers deliver. Despite several revered and distinguished software engineers coming up with funky principals and practices such as AOP it's not always easy to translate into a team of developers (or a solution). In fact some developers thought they understood the more complicated aspects to software engineering and bludgeoned into the framework any fancy theory they thought they understood. The simple fact is though, we didn’t need any complicated solutions, the simple stuff and use of popular design patterns gave us everything we needed.

    Fortunately for you being at Microsoft you get to work with some of the most talented guys in the business so the same problem should never be an issue. Good luck with the MVC, thank you for sharing such intimate details about the development. I am very much looking forward to MVC which I hope to learn about at this years PDC.

Comments have been disabled for this content.