FubuMVC’s Internal Runtime, the Russian Doll Model, and how it compares to ASP.Net MVC and OpenRasta

I’m blogging out some preliminary FubuMVC documentation in preparation for a workshop later this week.  As a logical place to start, I’d like to dive into the internals of how FubuMVC handles an Http request.  Along the way I’ll try to explain how our design philosophy and goals led us to the current shape of FubuMVC.  A typical user of FubuMVC wouldn’t come into contact with any of these details in daily usage, but I think it’s still good to know what’s going on under the hood.  I’m also going to contrast the internal design of FubuMVC to ASP.Net MVC and a tiny bit to OpenRasta.  For as long as this post is, I’ve left out a lot of important details, so look for more content soon-ish to follow up on tangents.

Request Responsibilities

First, let’s stop and think about some of the typical stuff that needs to happen to handle an Http request.  Granted, this varies quite a bit, but the typical Http request in our application is doing something like these steps:

  1. Start up a new nested StructureMap container and an NHibernate session (if necessary)
  2. Apply any authorization rules and potentially reject the request with a 403 response
  3. Model binding.  Take the request data and transform the raw Http request into something more palatable like a simple DTO / ViewModel object that represents a request for either a view or the invocation of a service.  In fubu parlance I call this the input model.
  4. Pass the input model object into the correct action method and get the resulting output model
  5. Take the output model object and either use it to render the correct view or simply write the output model to the output stream as Json.
  6. Clean up and dispose the NHibernate session and the nested StructureMap container for the request (this will be important later)

Contrasting FubuMVC and ASP.Net MVC

The key technical differentiator between FubuMVC and ASP.Net MVC or MonoRail is our usage of composition instead of inheritance as a way to configure and extend the request pipeline.  ASP.Net MVC and some other .Net web frameworks rely on subclassing and extending a Controller base class to directly coordinate the steps I outlined in the section above.

The controller methods (actions) in ASP.Net MVC force you to return an object that implements the IActionResult interface telling the controller what to do next (render this view, spit out json, issue a redirect, etc.).  I liked this model at first because it’s simple to understand and use, but I think you lose a lot of flexibility, reuse, and modularity.  The IActionResult model introduces a tight coupling in the pipeline between controller actions and outputs.

FubuMVC grew out of our frustrations with using the inheritance based model of ASP.Net MVC and what we felt was unnecessary repetition from the IActionResult usage.  We postulated that we would be more successful if the responsibilities in that list could be spread out into a series of cohesive little objects that were almost completely decoupled from each other.  We eventually settled on the name “behavior” to describe these little classes.

Behaviors and the Russian Doll Model

Behaviors in FubuMVC are modeled by this key abstraction in the FubuMVC runtime:

public interface IActionBehavior
{
void Invoke();
void InvokePartial();
}

You might notice that the IActionBehavior’s methods don’t take in any arguments for request state (i.e., no RequestContext/ControllerContext).  FubuMVC depends on an IoC container to put the right dependencies including request state / response abstractions into the behavior objects through good old fashioned constructor injection.

A single Http request would potentially be handled by a combination of behaviors, each fulfilling a single responsibility.  A simplified view of a FubuMVC request looks like this:

diagram (1)

To circle back to the section on “request responsibilities” and map that to behaviors, a typical request in my team’s system might be handled by:

  1. A “TransactionContainerBehavior” that starts the nested container and the NHibernate session for the request.  This behavior also handles the session and nested container cleanup after the other behaviors run.  See the following paragraph on the “Russian Doll Model” for a full explanation.
  2. The “AuthorizationBehavior” runs any and all authorization rules relevant to this request
  3. An “ActionCall” behavior that “knows” how to invoke the proper action by first exercising the model binding services in fubu to turn the raw Http input into something usable by the action.  The ActionCall then executes the proper action and stores any output where other behaviors could get to it.
  4. An “output handler” behavior of some sort that can find the output of the action and render a view or serialize the output to json or just write a status code to the Http response.

One of the key features of the behaviors model is what some folks have taken to calling the “Russian Doll Model” (think about the traditional nesting dolls like these).  In practice, behaviors are nested inside other behaviors as shown by this base class we use for most of the out of the box behaviors in FubuMVC:

    public abstract class BasicBehavior : IActionBehavior
    {
        private readonly Action _partialInvoke;
 
        protected BasicBehavior(PartialBehavior partialBehavior)
        {
            _partialInvoke = partialBehavior == PartialBehavior.Executes
                ? (Action) (Invoke)
                : () => { if (InsideBehavior != null) InsideBehavior.InvokePartial(); };
        }
 
        public IActionBehavior InsideBehavior { get; set; }
 
        public void Invoke()
        {
            if (performInvoke() == DoNext.Continue && InsideBehavior != null)
            {
                InsideBehavior.Invoke();
            }
 
            afterInsideBehavior();
        }
 
        public void InvokePartial()
        {
            _partialInvoke();
        }
 
        protected virtual DoNext performInvoke()
        {
            return DoNext.Continue;
        }
 
        protected virtual void afterInsideBehavior()
        {
        }
    }

 

Classes that inherit from the BasicBehavior superclass can implement template methods to perform work before the inner behavior, decide whether or not to execute the inner behavior, and run some code after the inner behavior runs.  If you forgo the superclass above and write a behavior directly to the IActionBehavior interface you gain the ability to use try/catch or using blocks around the invocation of the inner behavior like this example:

    public class StopwatchBehavior : IActionBehavior
    {
        private readonly Action<double> _record;
 
        public StopwatchBehavior(Action<double> record)
        {
            _record = record;
        }
 
        // The underlying IoC container would inject the "inner"
        // behavior via this property
        public IActionBehavior InnerBehavior { get; set; }
 
        public void Invoke()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                InnerBehavior.Invoke();
            }
            finally
            {
                stopwatch.Stop();
                _record(stopwatch.ElapsedMilliseconds);
            }
        }
 
        public void InvokePartial()
        {
            Invoke();
        }
    }

My favorite usage of this nesting capability is how easy it makes it to clean up resources like containers or NHibernate sessions opened up during a request.

Other differences between ASP.Net MVC and FubuMVC’s “Behaviors”

  1. FubuMVC has no concept of a “Controller, ” only actions that at this point must conform to the “one model in, one model out” design principle.  Action classes can happily be POCO classes with zero coupling to any FubuMVC namespace.
  2. You can chain multiple actions in the same request.  We’ve taken advantage of this ability to greatly simplify our “crud” actions by reusing actions across routes.  Other teams are using chained actions to generalize validation.
  3. There’s technically no reason why a request has to include an action at all.  “Controllerless” actions were trivial in FubuMVC to implement and save you from wasting your time on controller actions that just return their input model.
  4. The IActionFilter from ASP.Net MVC is just a behavior in FubuMVC.  The FubuMVC behaviors model is much simpler than ASP.Net’s architecture but we think the simplification leads to more capabilities rather than less.  I definitely think the nested behavior model in FubuMVC makes “before and after” functionality much easier and cleaner to implement than the IActionFilter abstraction in ASP.Net MVC.  Hat tip to Chad Myers[LINK] for that idea.  Hey, it’s all about behaviors these days…
  5. FubuMVC uses an IoC container to assemble all the behaviors for a single request at one time and all their dependencies.  You might note that the IActionBehavior interface above doesn’t use any kind of “*****Context” objects to pass state around because all the Http abstractions and the model state is pushed into behaviors or services through dependency injection.  This is a full topic in its own right, but I’m very convinced that using an IoC container inside FubuMVC has greatly simplified the internals compared to the same functionality in the ASP.Net MVC codebase.

FubuMVC v. OpenRasta

I do think FubuMVC is superficially similar to OpenRasta in terms of architecture, especially in regards to composition over inheritance.  However, I think the runtimes and approaches are different enough to justify the separate efforts.

I think you could say that OpenRasta’s IPipelineContributor is analogous to FubuMVC’s IActionBehavior, but they diverge after that.  Whereas FubuMVC relies on that “Russian Doll Model” at configuration time to pre-order the behaviors for each request, OpenRasta uses the IPipeline / PipelineRunner class as a “Coordinator” for the various IPipelineContributor’s (see this MSDN article for an explanation of Coordinator).  To contrast them, I’d say that FubuMVC solves the composable pipeline problem with a complex configuration model and a simple runtime model where I’d say that OpenRasta has a more complex runtime model but a simpler configuration model.  Again, just my thoughts on the two tools.

Maybe what I should say is that if you share the general ALT.NET aesthetic, I think you’d like either OpenRasta or FubuMVC much better than ASP.Net MVC.  If you’re a serious student of software design, I’d highly recommend doing your own compare and contrast study of all three tools and how they solve the same problems in different ways.

The configuration approach is also very different, but I have click “publish” on this blog post at some pointWinking smile

So, why did we do it this way?

One of the key design philosophies of FubuMVC from day one has been to strongly favor composition over inheritance in regards to the internal code structure and how we expose extension points to users.  Of course “composition” is just the means.  The real design goals of FubuMVC were to

  1. Decouple application code from the framework as much as possible.  I really don’t want framework junk like “ActionResult’s” or attribute noise to intrude into my application code.  Composition with little cohesive objects has been a powerful way to remove the framework goop so prevalent in inheritance based frameworks.
  2. Enable convention over configuration in a meaningful way.  A lot of the conventions in FubuMVC work by looking at a chain of behaviors and using some criteria to determine what other behaviors should be chained to the original behaviors (like attaching “view” behaviors to action behaviors based on a naming convention).  Being able to move around little behavior objects like Lego pieces enables convention based approaches in FubuMVC. 
  3. Eliminate as much repetitive code as possible.  Why should I have to tell the dang framework what view to render if it could figure it out from a convention?
  4. Maximize our ability to extend and/or control the way server requests are handled.  Think about the simple need to intercept the output from an action before it’s rendered.  That’s simple with our behaviors model, but the equivalent solution in ASP.Net MVC requires some non-obvious hoop jumping.

Some of you (and seemingly all of Microsoft’s DevDiv group) might disagree with me, but my very strong feeling is that extensibility and reuse through composition is more powerful than inheritance.  I think composition based code is easier to understand and debug because the boundaries and dependencies between sections of code are clearer than the equivalent code using inheritance with lots of template methods.

What I need to talk about next….

I think by now I’ve reminded all of you long term readers that my blog is no place for folks with short attention spans.  Some related topics I’ll cover later are:

  • Someday I’d like to share some of Dovetail’s experiences with customer extensions and how the behavior model allows us to be “Open/Closed” with the customer extensions
  • How FubuMVC uses Routing as a Front Controller
  • How FubuMVC abstracts Http (I’m thinking ahead a little bit to possibly supporting OWIN if that ever feels like more than an academic exercise).
  • How FubuMVC uses an IoC Container
  • FubuMVC’s Configuration Model – you probably noticed that I didn’t remotely explain how behaviors are configured in FubuMVC
  • How FubuMVC Model Binding Works

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in DI, FubuMVC, IOC, StructureMap. Bookmark the permalink. Follow any comments here with the RSS feed for this post.