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.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in ASP.NET MVC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • lubos

    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

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

  • http://vladan.strigo.net Vladan Strigo

    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

  • http://codeclimber.net.nz Simone

    > 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?

  • Paco

    “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…

  • http://joshuaflanagan.lostechies.com Joshua Flanagan

    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?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • http://www.sullivansoftdev.com/blog Brian Sullivan

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

  • http://rayaspnet.wordpress.com Ray

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

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • Paco

    @Jimmy Bogard: I mean the action filter attribute

  • Erik

    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

  • http://lunaverse.wordpress.com Tim Scott

    What does this do?

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

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Paco

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

    public SomeFilterAttribute(IDependency dep) { _dep = dep; }
    public SomeFilterAttribute() : this(Container.Resolve()) {}

    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.

  • http://webgambit.com Karthik Hariharan

    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.

  • http://www.cristianprieto.com cprieto

    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

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

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • http://vijay.screamingpens.com Vijay Santhanam

    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?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • http://richarddingwall.name Richard

    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 :)

  • http://mrpmorris.blogspot.com Peter Morris

    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!

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @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)

  • http://mrpmorris.blogspot.com Peter Morris

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

  • Eric

    @Peter

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

    http://code.google.com/p/codecampserver/

  • shrkfish

    .net MVC

  • Martin Nilsson

    “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?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

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

  • http://nesteruk.org/blog Dmitri

    You mention that you execute 4 browsers during WatiN tests, but my experiments show that multithreading WatiN is generally a bad idea and leads to exceptions almost instantly. Can you elaborate a bit on which Browser object you are using and how? Thanks.

    Dmitri

  • Benjamin Eidelman

    you say you use Automapper to go from Domain to EditModel, can you give some info on how you put changes back into original Domain entities?
    ie: update property -> property, and update children collections, ie. add new rows, delete not existent rows, update modified rows, and different update task like these.
    AFAIK AutoMapper can’t do that, you do it manually?Thanks a lot Jimmy!

    • Anonymous

      Sometimes, just setters. Sometimes, I don’t expose setters on the domain object and I have methods to mutate.

  • Rob Sylvester

    I really like your InputFor concept can you post the source code for it?

  • Whoever

    This InputFor(something).AsDropdown with integrated custom attribute sounds like a very elegant solution. Any chance let us take a peek?

  • Pingback: Cómo implementar aplicaciones con ASP.NET MVC (Recomendaciones) | Kash.Core