Making the case for MVC3 (I'm now a believer)

A few years ago a co-worker sang the praises of ASP.NET MVC, lobbying for its adoption by the team. I certainly struggled with it. Part of that was me, part of it was MVC being in Beta at the time and having all sorts of quirks. But things have changed. Thanks to MVVM and Silverlight I'm very comfortable with the concepts, and the MVC framework has since matured by leaps and bounds. And now, after re-writing Checksum Labs using MVC3, I can't imagine going back to Web Forms.

To be clear, this isn't a deep-dive into MVC. This is a high-level, plain-English overview for those who haven't scratched beyond the surface (and whatever your reasons are, I've probably shared those sentiments at some point). You'll find countless blogs explaining the "how" and "what" of MVC, but it's a little tougher to find explanations of the why so I'll give it a shot.

"Separation of Concerns"
Unless this is Day 1 of your software development journey you've likely heard this phrase by now. MVC is where you get to see it in action. But let's back up a second. If you've done any work with ASP.NET Web Forms you're familiar with the traditional page:

Web Forms Page

Clearly there are benefits to the Web Forms approach. A lot of the plumbing is tucked away by ASP.NET. Tracking the state of controls is a no-brainer. Once you tag controls with a "runat=server" the code just knows about them and everything just works. Binding collections to Repeaters and DataGrids couldn't be easier.

But there's a whole lot of magic going on to make things appear seamless. The ASP.NET runtime sits and waits for IIS to pass requests on through so it can start executing some code. But it's not just the code associated with one particular page; it must account for code associated with master pages, user controls, and any other dependencies that contribute toward the final result. All of this translates to a fairly convoluted weaving of "page life cycles." A standalone page isn't a big deal, but an enterprise application is likely to contain nested master pages and user controls which may or may not interact with each other. All of these pieces have their respective cycles and it can quickly become a challenge to manage them, to identify which events should be hooked into and when.

Another problem is the tight association between the code and presentation. You might ask, "Is that really a problem or do architectural snobs just like to impose their will on others?" Well, my answer is "both" but let's focus on the issue at hand. Let's take a look at a simplified view of the MVC world:

MVC workflow

It might initially seem more complicated than Web Forms, but here's the win: we've broken free of the convoluted "page life cycle." Controllers, which consist solely of code, are now the point of entry. The pipeline is much more straightforward and the pieces have distinctive roles:

  • The Controller fields the incoming request and interacts with other layers of the application (the number of layers will depend on how much abstraction you feel is necessary). Nothing complicated should be going on in the Controller. No business logic. Its primary role is to construct Models (ViewModels, really) and pass them to the View. As the saying goes, keep your Controllers thin and your Models fat.
  • The Service layer is typically a set of classes responsible for performing validations. For example, a user posts form data to update a Customer. The Service layer is the gatekeeper which makes sure that updated Customer object passes the sniff test. If it does, the Service passes it on to the Repository layer.
  • The Repository layer is where your Model entities are constructed or manipulated in conjunction with the underyling data store. That's the short version. There are plenty of ways to abstract this layer even further depending on where your data store is ultimately located.
  • The ViewModel is a strongly-typed object passed from the controller to the view, even though it's often referred to (even by IntelliSense) as the "Model." I recommend keeping a distinction between your true "Model" entities and the ViewModel, which is a class designed to contain only the data necessary to service a view (and potentially any partial views belonging to it). Here again the idea is to keep the ViewModels as thin as possible.
  • The View is your HTML markup with just enough script sprinkled in to glue it all together. The goal is to keep views nice and lean. At first glance you might balk at the markup as it resembles Classic ASP, but it quickly becomes evident that you've got granular control of your markup. In the Web Forms world you're forced to rely on ASP.NET to magically convert controls into markup; that really becomes a headache when you need to override certain aspects of that rendering pipeline. In the MVC world, there's no such magic or mystery. The markup you author is the markup you get. No blobs of ViewState, no "runat" attributes, no surprises in your finalized markup.

To drive it all home: in the Web Forms world you have a code-behind page doing more than it really should — fetching data, potentially performing validations, binding controls, being the hub of everything that matters. In the MVC world there's a clear set responsibilities across each layer.

View Interchangeability
With our Views nice and lean in the MVC world, we can start to reap the benefits. If we decide it's time for a mobile version of our site, good news! We don't need to reinvent the wheel. There's an excellent chance we won't need to touch any of the code in the Service, Repository, or Model layers. The changes in the Controllers are likely minimal as well, perhaps just a matter of changing a ControllerBase class to detect mobile clients and set the Views route accordingly. And really, shouldn't it be this simple? The nature and intent of your application doesn't change regardless of the device it's viewed on — only the presentation should be subject to change, and that flexibility is one of the biggest things you've won in MVC.

Testability
I'll be 100% honest: I'm not a testing zealot. It bores me to tears, or at least mindless unit testing does (I see far more value in integration testing but I digress). But the bottom line is, testing is going to save your bacon some day. Thanks to the clear separation of concerns, testing is very straightforward in MVC. The Controllers, Service, and Repository are easily testable as they're nothing but straight-up code. In fact, nothing's stopping you from testing the actual views themselves, since the view engines can be instantiated as part of a test. Note: I personally don't see much value in that, but hey, knock yourself out.

Tangent: if you're going to leverage the test-related benefits of MVC, you might as well go the extra mile and get comfortable with "Dependency Injection" and "Inversion of Control." These terms sound horrendously daunting but they're actually simple concepts. The plain English translation in the context of MVC: rather than your Controllers having private members that are concrete instances of Service or Repository objects, make interfaces instead (ICustomerService, ICustomerRepository, etc.) and use those as the members. Add a constructor to your Controller which accepts those interfaces as parameters.

Now you can create fake Service/Repository objects (which implement those interfaces) and pass them into the Controller at test time. You're effectively deciding for the Controller, based on the context of testing vs. the real deal, what its Service and Repository members are. This "inversion of control" allows you to remove the unpredictability of outside layers and lets you isolate those things you want to truly test. And so, you'd repeat this process for the Service and Repository and so on, or at least until you get bored with testing and succumb to the temptation of writing "actual" code ;)

Other Benefits
Surely you're sold on MVC by now (just let me believe it, ok?), but there are a couple other takeaways to mention:

  • SEO (search engine optimization): given MVC's routing capabilities you can structure your URLs like this: http://www.checksumlabs.com/apps/1/buddy-knavery-episode-1-the-killer-riffs. The first post-domain token in the URL ("apps") directs the request to the Apps controller. The second token ("1") passes an ID parameter to the default Action of that controller. So what's with the third token, the "buddy knavery" nonsense? In this case, it's actually optional and doesn't affect anything. Change "buddy-knavery" to "john-locke" and you'll see the exact same result. So why bother? Well, the theory is — and it's not without debate — that contextually "friendly" URLs can boost your search engine rank.
  • Scaffolding. If you get in the habit of constructing ViewModels that are passed to/from Controllers, you'll find it ridiculously easy to accommodate CRUD operations via forms on web pages. When you create a new View you can specify that it should be strongly typed, i.e. based off a Model/ViewModel you've already created. MVC builds a View with the Create, Edit, or Delete "scaffolding" injected right into the HTML markup. It took me approximately 15 minutes to build the back-end portion of Checksum Labs, that is, the UI to create and manage articles. I didn't need to map a bunch of fields, didn't need to sort through a bunch of form POST data. I set up my ViewModel once, MVC did all the rest.

So what now?
The Getting Started with MVC3 tutorial on www.asp.net is actually one of the best tutorials I've found for ramping up quickly. It's very straightforward and digestible, doesn't try to tackle the world all at once. You may be tempted to choose the ASPX view engine just for familiarity's sake, but the Razor view engine (selected by default) is a cleaner, leaner version of scripting with surprisingly good IntelliSense. You'll like it, I swear.

Once I pulled myself away from the Web Forms mindset, after just a bit of practice in passing ViewModels from controllers to views, MVC3 really became a pleasure to work with. I now understand those who say they feel "dirty" going back to Web Forms. That's not to say Web Forms are inherently bad or invalid — they're not — but MVC has a clear and significant edge in terms of organization, simplicity, elegance, and ease of testability.

Posted: 11/10/2011 10:21:28 PM

Comments
Alex
2/16/2012 2:50:15 PM
Thank you, thank you, THANK YOU for speaking out against the obsession with testing in MVC. I can't tell you how frustrated I am with the amount of tests I'm being recommended to write, as a developer getting to grips with MVC. I can't help but feel that the time I'm burning up writing tests would be far better spent writing solid production code in the first place.
Add Comment
Name (optional):
Comment:
Verify:
Articles By Subject
ASP.NET
Buddy Knavery Episode I
Buddy Knavery Episode II
Checksum Labs
LOST Redux
Miscellaneous
Retro Adventure Game Environment
Silverlight
Steel Saga
WinRT/Metro