Los Techies : Blogs about software and anything tech!

How we do MVC


Our team has been using the ASP.NET MVC framework on a production application for about 9 months now, and in that time, we’ve crafted a number of opinions on how we want to design MVC applications.  Much of this design is inspired by the concepts from FubuMVC, and in particular, Jeremy’s post on their MVC opinions.  Our approach is rather similar, but deviates in a few places.

I’ll elaborate on many of these concepts in future posts, but for now, in no particular order, our opinions:

  • Controllers are thin and light, only responsible for communicating an action result back to the MVC pipeline.  All major request-handling belongs in application-level services, that communicate operation results back up to the UI layer (controller), that then decides what to do with that result.  In many cases, the “service layer” is a simple repository call.
  • Strongly-typed views, and discouraging the dictionary part of ViewData.  There are edge cases where we have to use it, but we try very hard not to use magic dictionary keys.  It usually crops up around orthogonal view concerns, where we don’t want to pollute our “main” ViewModel with that information.
  • Distinct ViewModels separate from the domain.  Our entities aren’t built well for binding, whether it’s in a ViewModel (read-only) that throws NullReferenceExceptions in a “Something.Other.Foo.Bar” call, or in an EditModel (form) that uses a ModelBinder with very specific design requirements.  The View puts very real requirements on your ViewModel.  For many classes of applications, this is acceptable.  For others, with more complex domains, this influence is unwanted.  Creating a separated ViewModel provides a clean separation of concerns between View and Domain.
  • No magic strings.  None of this Html.TextBox(“Surprise.I.Am.Actually.A.Property”) business, expression-based syntax for everything that refers to a 1) class 2) method or 3) property.  This means:
    • Using expression-based form generation (Html.InputFor(order => order.Customer.Name)
    • Using expression-based URL generation (Url.Action<ProductController>(c => c.Index()))
    • Using expression-based RedirectToAction methods, similar to Url.Action

I can’t stress enough that last point.  If you have a string referring to a member on a type, use expressions!  String-based reflections leads to subtle runtime errors, where refactoring screws up your views and controllers.

  • Smart model binding.  Our model binders can bind:
  • Validation on your EditModel.  We use Castle Validators on our EditModel to do the rote “OMG this is totally not a number type lol” validation, range validation, required field validation, and so on.  Our validation occurs inside our model binder, before our action method gets called.  This isn’t done in any filter or anything like that.  We had to jump through a few hoops to merge the two concepts, as you have to match up Castle’s error summary with MVC’s concept of Model State, as well as taking care of nested levels of access (that Customer.Name example).
  • AutoMapper to go from Domain –> ViewModel and Domain –> EditModel.  This is again because the view and controller put constraints on our model that we didn’t want in our domain.  AutoMapper flattened our domain into very discrete ViewModel objects, containing only the data for our view, and only in the shape we want.
  • Very smart HTML generation.  We have exactly 1 method call for generating a form element, “Html.InputFor”.  As part of that “InputFor”, it examines an input specification, that collects the PropertyInfo, any attributes, the type, any modifiers called, and selects an appropriate InputBuilder.  Call InputFor(p => p.Id) and Id is a GUID?  That creates a hidden input element.  Call InputFor(p => p.Customer.Address) and Address is a complex type?  That looks for a partial with the same name of the type.  With all InputFor’s expression based and going through the exact same method, we can:
    • Create classes of inputs, such as radio buttons for enumerations, drop-downs for enumerations, date-time pickers for DateTimes, and so on
    • Automatically include standardized output for label elements and error information
    • Extend new input builders for specialized cases (plugged in through our IoC container), which matches based on a simple IsMatch(inputSpecification)
    • Standardized look and feel for all classes of inputs

We originally extended HtmlHelper for every new thing, but it got confusing what the right builder for what situation was.  Instead, we used exactly one method and stuck to easily discoverable modifiers (InputFor().AsDropDown()) or metadata on our EditModel (RequiredFieldAttribute automatically outputs a little asterisk next to the input).  It got rid of TONS of duplicated logic, and we had lots of flexibility in the granularity of our input elements, from a single textbox to a complex form that used a partial. The partials were especially nice to be tagged based on type, as things like an AddressForm required no new View work.  We just made sure our EditModel had an AddressForm as its type for any address (which is filled in by AutoMapper).

  • Standardized action method names.  RESTful, but not REST, which we don’t need in our context.
  • UI testing with WatiN and Gallio.  WatiN executes through Gallio, which is parallelizable (our quad-core build server executes 4 test classes, and therefore browsers at a time).  We use Gallio’s concept of test steps to output meaningful information about our tests.  We transform the Gallio output into an HTML report, which becomes part of our deliverables.  People paying us for our services want assurances that our application works, and want to know in a nice pretty way.  When a test fails, WatiN takes a screenshot, and we attach it to the failing test, which then shows up in our build report.  Let me repeat: our automated UI testing includes screenshots of failures, all in the background, with no human intervention.
  • UI tests use our ViewModel and EditModel types, along with expressions, to locate and validate elements.  We do things like ForSection<ProductDto>.LocateRowWith(p => p.ProductName, “SomeName”).CheckValue(p =>p.Total, 15.6m).  It’s all strongly-typed, and refactoring-friendly.  All ViewModel data is wrapped in SPAN tags with predictable CLASS attributes based on the expression.  All INPUT element IDs and NAMEs are generated from the expression as well.  WatiN uses the exact same mechanism to generate a string name, ID or class from an expression, resulting in compile-safe UI testing.  When we delete a field from a view, we remove it from our ViewModel, so our UI test won’t even compile.  It’s all very refactoring-friendly.
  • Actions receiving a POST receive the EditModel as an action parameter, not some collection object.  This isn’t ASP 3.0 anymore, we don’t need Request.Form.
  • Use partials when you have common markup, and the data is in your top-level ViewModel object.
  • Use RenderAction when you have common markup, but the information is orthogonal to the main concern of your view.  Think like the “login widget” at the top of every screen.  A filter is too much indirection for that scenario, RenderAction is very explicit.
  • No behavior in filter attributes.  Attributes are for Metadata, not Metabehavior.  Delegate the real work of the filter attribute to a filter class.  You can’t control the instantiation of a filter attribute, and that gets annoying if you want do to dependency injection.
  • jQuery.  Nothing else to say here, except it really helps to do some actual JavaScript and jQuery learning here.

Like many of the frameworks coming out of Redmond, MVC is not an opinionated framework.  Rather, it is a framework that enables you to form your own opinions (good or bad).  It’s taken quite a long way, with a very stable result at the end.  It’s certainly not perfect, and there are a few directions we’d like to go differently given the chance.  In the next few posts, I’ll elaborate with real examples on the big examples here, otherwise, I’d love to have people poke holes in our approach.

Kick It on DotNetKicks.com
Posted Apr 24 2009, 12:09 AM by bogardj
Filed under:

Comments

DotNetShoutout wrote How we do MVC - Jimmy Bogard -
on 04-24-2009 12:16 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

lubos wrote re: How we do MVC
on 04-24-2009 12:45 AM

excellent stuff. obviously you depend on reflection to reduce a lot of boilerplate code and taking advantage of static typing but could you also write about sections of code where you're still not happy in your project?(necessary code duplication, error-prone boilerplate code) and don't have solution at the moment?

it looks like you're working on a project where every single line is fun to work on but I don't believe it's all so perfect... please talk about the dark side as well :-)

BjartN wrote re: How we do MVC
on 04-24-2009 3:06 AM

Good stuff. I would be interested in hearing more about how you do your UI tests.. From a to z ;)

Reflective Perspective - Chris Alcock » The Morning Brew #334 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #334
on 04-24-2009 3:32 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #334

Vladan Strigo wrote re: How we do MVC
on 04-24-2009 4:26 AM

Great stuff!

It would be interested to hear more about "InputFor", sounds like a great idea to kill boilerplate code...I would be especially interested in hearing how you deal with checkbox lists, radio button lists, inputs with choosers (popups), etc... (anything more than just showing a textbox actually).

Thanks!

Vladan

Simone wrote re: How we do MVC
on 04-24-2009 6:36 AM

> No behavior in filter attributes.  Attributes are for Metadata, not Metabehavior.

> Delegate the real work of the filter attribute to a filter class.  You can’t control the instantiation of a filter attribute, and that gets annoying if you want do to dependency injection.

So you basically use the filter attribute just a "wrapper" to the filter class? Or you don't use the concept of ActionFilters at all?

Are you doing it only because the default action invoker doesn't allow injecting deps? Can't this be easily solved writing your own ActionInvoker? (I did it for Ninject 1, and Ninject 2 comes with that out of the box). Or are you doing it for other reasons as well?

Summary 23.04.2009 « Bogdan Brinzarea’s blog wrote Summary 23.04.2009 &laquo; Bogdan Brinzarea&#8217;s blog
on 04-24-2009 8:06 AM

Pingback from  Summary 23.04.2009 «  Bogdan Brinzarea’s blog

Paco wrote re: How we do MVC
on 04-24-2009 8:42 AM

"You can’t control the instantiation of a filter attribute, and that gets annoying if you want do to dependency injection."

You cannot control the instantiation, but you can apply constructor injection before the execution.

Nothing is wrong with injecting dependencies into your action filters as far as I know...

Joshua Flanagan wrote re: How we do MVC
on 04-24-2009 8:45 AM

I'd like to hear more about why you automatically generate the HTML element IDs. Isn't the ID only relevant to client (javascript) code? Don't you end up getting the opposite result - in that a server-side refactoring that automatically changes the ID will break client side code? Presumably, not every element needs an ID, only the few that need to be targeted by some client logic. We have a method that hangs off of our INPUT generators that lets you specify a string ID (in the view, where it is close to the client code that needs it), exactly for this reason. What are we missing out on?

bogardj wrote re: How we do MVC
on 04-24-2009 8:56 AM

@Simone

Yeah, we have two classes - the filter attribute and the actual filter.  The attribute calls the actual filter, but uses IoC to instantiate it.

@Paco

Nothing wrong with your action filter, but action filter attributes, yes.

@lubos

The dark side is mainly around all of the hard lessons we learned to get to this point.  Our application wasn't originally all strongly typed, and it was not an insignificant technical debt payment to slowly convert it to strongly typed (views, controllers, UI tests).  There are definitely some things I would like to do differently, but we're too far along to change.

bogardj wrote re: How we do MVC
on 04-24-2009 9:26 AM

@Joshua

Nothing really, we're just in a different situation.  IDs spit out by built-in MVC input generators are not XHTML-compliant, which is a requirement for our situation.  Our jQuery IDs are then expression-based as well.  Throw in issues where one partial is a single element, but actually lives in an array-of-array-of-arrays, so we found it easier to just generate all IDs, and not decide them at will, make them predictable.  Think along the lines of a single form having x => x.LocationHistory[2].Phone[3].Number.  On the top form, it's x => x.LocationHistory.  On the next form, it's x => x.Phone.  Finally, it's x => x.Number.

We also use it in our UI testing.  Radio buttons are tough to select a single value by name, so predictable IDs helped us there.  It was a bit of a journey how we got to that point, and I doubt it applies to many other situations.

Brian Sullivan wrote re: How we do MVC
on 04-24-2009 11:50 AM

The stuff with Watin and Gallio sounds very interesting.  Is there any other information out there about setting this kind of thing up?  I think it might be useful whether you're using MVC or not. (Which my employer is not, yet.)

Ray wrote re: How we do MVC
on 04-24-2009 2:28 PM

" it is a framework that enables you to form your own opinions "

I do not think so.  In MVC, too many things limit your freedom:

MVC design patthern/JQuery/StructureMap, Dependency Injection and Inversion of Control.

Only thing left, it is your ablity to control html tag.

bogardj wrote re: How we do MVC
on 04-24-2009 3:11 PM

@Brian

I'll elaborate on that in a future post, but it's frickin awesome what Gallio supports.

@Ray

Isn't everything in MVC about creating the HTML tag? :)  Probably "conventions" is a better term there.

Arjan`s World » LINKBLOG for April 24, 2009 wrote Arjan`s World &raquo; LINKBLOG for April 24, 2009
on 04-24-2009 3:46 PM

Pingback from  Arjan`s World    » LINKBLOG for April 24, 2009

Paco wrote re: How we do MVC
on 04-24-2009 4:21 PM

@Jimmy Bogard: I mean the action filter attribute

Erik wrote re: How we do MVC
on 04-24-2009 4:30 PM

Jimmy,

  How do you handle your Action/Argument Based Operations.  Something Simple Like

Entity.DoSometing(int a, ortherentity b,  string c) ?

Do you Create a an EditModel or Form that would Represent the Action?  I am not sure I would consider this an EditModel, but am guessing that Is what you would do for this and then make the relevant service call?

Thanks,

Erik

Tim Scott wrote re: How we do MVC
on 04-24-2009 6:18 PM

What does this do?

ForSection<ProductDto>.LocateRowWith(p => p.ProductName, “SomeName”).CheckValue(p =>p.Total, 15.6m)

Perhaps you will elaborate in a future post.  If you do, maybe I shouldn't read it as it will only make me very jealous.  I'm currently developing WatiN/Gallio tests for a WebForms app, and so I'm stuck with ID-ends-with-find-by-strategy.

Jeremy D. Miller -- The Shade Tree Developer wrote My Quick Oversimplified ASP.Net MVC Pros and Cons
on 04-24-2009 8:54 PM

There's been a lot bouncing back and forth about whether the ASP.Net MVC (and the MVC manner of building

bogardj wrote re: How we do MVC
on 04-25-2009 9:22 AM

@Paco

To be specific, I'm talking about this, in the attribute ctor:

public SomeFilterAttribute(IDependency dep) { _dep = dep; }

public SomeFilterAttribute() : this(Container.Resolve<IDependency>()) {}

We do the resolving in the body of the attribute, but we still want to use ctor injection in the filter, instead of the filter attribute.

Karthik Hariharan wrote re: How we do MVC
on 04-25-2009 11:59 AM

So how much of this made it into MVC In Action? I understand a lot of these conclusions/conventions came about over the last 9 months, but I'm curious where your book's content overlaps with this information.

cprieto wrote re: How we do MVC
on 04-25-2009 6:21 PM

Where the automapper magic takes place? do you permeate your View/Edit Model into your Service Layer (allowing your SL do the autommapping) or they stay only reaching your controller? (your SL only see common Domain Objects, your Controller do the mapping stuff)?

I'm curious....

Beyers wrote re: How we do MVC
on 04-25-2009 6:47 PM

I too would like to see some more information on the “Html.InputFor” method. This sounds like a really neet way to do things.

Jeffrey Palermo (.com) wrote You should NOT use ASP.NET MVC if. . .
on 04-26-2009 3:21 PM

You are not very comfortable with polymorphism You aren’t willing to build on top of the framework You rely on 3rd party vendor controls for lots of the UI You are adverse to using open-source libraries The above are four quick reasons why WebForms

bogardj wrote re: How we do MVC
on 04-26-2009 8:15 PM

@Karthik

Quite a bit, but not all.  But with what we're writing about, plus what we couldn't get to in CodeCampServer, it's all there.

@Cristian

That deserves a post all on its own, I'll work on that one soon.

ASP.NET MVC Archived Blog Posts, Page 1 wrote ASP.NET MVC Archived Blog Posts, Page 1
on 04-27-2009 12:04 AM

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

igorbrejc.net » Fresh Catch For April 27th wrote igorbrejc.net &raquo; Fresh Catch For April 27th
on 04-27-2009 7:02 AM

Pingback from  igorbrejc.net » Fresh Catch For April 27th

Vijay Santhanam wrote re: How we do MVC
on 04-27-2009 10:44 AM

Great Post!

Hmm, so many questions.

I guess the big two are:

* Do your master pages require view data? If so, how do u find common base View model class hierarchy? I found this annoying and opted for FilterAttribute injection instead.

* Speed. We found expression url generators quite slow and switched to Url.Action() instead. Do you do caching for this?

bogardj wrote re: How we do MVC
on 04-27-2009 11:33 AM

@Vijay

Do your master pages require view data? -> No, we solved this with RenderAction, which we thought solved the problem in a cleaner manner.

Do you do caching for this? ->  The database and network is still our biggest performance bottleneck, by an order of magnitude at least.  When that's no longer the case, we'll address the reflection implications.

Richard wrote re: How we do MVC
on 05-02-2009 3:57 AM

Interesting article - this is very similar to what my team has settled on for ASP.NET MVC apps too. We haven't formalized separate EditModels yet though - this is perhaps something we should look at. And automated browser testing via Watin/Selenium is always on the todo list :)

Peter Morris wrote re: How we do MVC
on 05-02-2009 6:41 AM

Do you agree that putting validation on the edit model is duplicating the validation in your entities?  What if a property on your entity moved from permitting null to not permitting null?  You'd surely have to update lots of edit model classes?

All very interesting though.  I'd love to see a (small as possible) demo!

bogardj wrote re: How we do MVC
on 05-02-2009 11:19 AM

@Peter

I have different kinds of validation in different places.  We'll have message/form-level validation (is this a number, a real date, etc.), which we call "invariants", then business-rule validation in our operation/message handler/service classes.  Think something along the lines of a form getting turned into a message, handled by a command object, which is responsible for business-rule validation as well as execution of the command.  So no, we really have zero duplication along that front.  But there is also zero duplication in our UI for manipulating a given field (99.9% of the time, anyway)

Peter Morris wrote re: How we do MVC
on 05-03-2009 5:45 AM

So any chance of seeing an example app?  It would be very interesting!

Eric wrote re: How we do MVC
on 05-07-2009 11:12 AM

@Peter

You might take a look at Code Camp Server (referenced above) --

code.google.com/.../codecampserver

I’m Down With M.V.C., Yeah You Know Me! wrote I&#8217;m Down With M.V.C., Yeah You Know Me!
on 05-10-2009 9:47 PM

Pingback from  I’m Down With M.V.C., Yeah You Know Me!

PhatBoyG (Chris Patterson) wrote I'm Down With M.V.C., Yeah You Know Me!
on 05-10-2009 9:48 PM

Over the past week, I've had some time to dig into the new ASP.NET MVC framework . For starters,

How we do MVC « using … wrote How we do MVC &laquo; using &#8230;
on 05-18-2009 6:40 AM

Pingback from  How we do MVC « using …

Bookmarks for 26.05.2009 through 27.05.2009 » mafflog wrote Bookmarks for 26.05.2009 through 27.05.2009 &raquo; mafflog
on 05-31-2009 11:05 AM

Pingback from  Bookmarks for 26.05.2009 through 27.05.2009 » mafflog

How we do MVC - Jimmy Bogard - wrote How we do MVC - Jimmy Bogard -
on 06-06-2009 3:15 AM

Pingback from  How we do MVC - Jimmy Bogard -

shrkfish wrote re: How we do MVC
on 06-06-2009 12:33 PM

.net MVC

VS2010学习 wrote ASP.NET MVC Best Practices - More ASP.NET MVC Best Practices
on 06-18-2009 11:09 PM

Wednesday, April 01, 2009 10:44 PM kazimanzurrashid   [Also check out the next part of this series

Jurnal de programator wrote ASP.NET MVC si Validarea Model Binder
on 06-22-2009 8:03 PM
Jimmy Bogard wrote How we do MVC – View models
on 06-29-2009 11:06 PM

A while back, I went over a few of the patterns and opinions we’ve gravitated towards on our current

AutoMapper in NerdDinner wrote AutoMapper in NerdDinner
on 07-06-2009 7:02 AM

Pingback from  AutoMapper in NerdDinner

Martin Nilsson wrote re: How we do MVC
on 11-29-2009 9:11 AM

"our automated UI testing includes screenshots of failures, all in the background, with no human intervention"

How do you hook into the test runner to create the screen shot? Do you have a plugin for the runner, like a nunit EventListener.UnhandledException? Or do you have an exception handler in your test methods?

bogardj wrote re: How we do MVC
on 11-29-2009 4:45 PM

@Martin

Watin lets us take screenshots of the IE window, and Gallio lets us embed images.  Put the two together - presto!  We hook up into the TearDown event and check the test status - Gallio lets us do this as well.

Dev Links « Blogosphere wrote Dev Links &laquo; Blogosphere
on 12-26-2009 8:19 PM

Pingback from  Dev Links « Blogosphere

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems