FubuMVC Lessons Learned — Magic Conventions, Russian Dolls, and Ceremonial Code

tl;dr: FubuMVC stressed concise code and conventions over writing explicit code and that turns out to be polarizing

The typical way to be successful in OSS is to promote the hell out of your work before you give up on it, but I’m under a lot less pressure after giving up on FubuMVC and I feel like blogging again.  Over the next couple months I’m going to write about the technical approach we took on FubuMVC to share the things I think went well, the stuff I regret, how I wish we’d done it instead, discarded plans for 2.0, and how I’d do things differently if I’m ever stupid enough to try this again on a different platform.

 

Some Sample Code

So let’s say that you start a new FubuMVC project and solution from scratch (a topic for another blog post) by running:

fubu new Demo --options spark

You’ll get this (largely placeholder) code for the MVC controller part of the main home page of your new application:

namespace Demo
{
        // You'd generally do *something* in this method, otherwise
        // it's just some junk code to make it easier for FubuMVC
        // to hang a Spark or Razor view off the "/" route
        // For 2.0, we wanted to introduce an optional convention
        // to use an "action less view" for the home page
	public class HomeEndpoint
	{
		public HomeModel Index(HomeModel model)
		{
			return model;
		}
	}
}

To make things a little more clear, fubu new also generates a matching Spark view called Home.spark to render a HomeModel resource:

<viewdata model="Demo.HomeModel" />

<content:header></content:header>

<content:main>
Your content would go here
</content:main>

<content:footer></content:footer>

The code above demonstrates a built in naming convention in FubuMVC 1.0+ such that the home “/” route will point to the action “HomeEndpoint.Index()” if that class and method exists in the main application assembly.

Some additional endpoints (FubuMVC’s analogue to Controller’s in MVC frameworks) might look like the following:

    public class NameInput
    {
        public string Name { get; set; }
    }

    public class Query
    {
        public int From { get; set; }
        public int To { get; set; }
    }

    public class Results { }

    public class MoreEndpoints
    {
        // GET: name/{Name}
        public string get_name_Name(NameInput input)
        {
            return "My name is " + input.Name;
        }

        // POST: query/{From}/to/{To}
        public Results post_query_From_to_To(Query query)
        {
            return new Results();
        }
    }

 

What you’re not seeing in the code above:

  • No reference whatsoever to the FubuMVC.Core namespace.
  • No model binding invocation
  • No code to render views, write output, set HTTP headers
  • No code for authentication, authorization, or validation
  • No “BaseController” or “ApiController” or “CoupleMyCodeVeryTightlyToTheFrameworkGuts” base class
  • No attributes
  • No marker interfaces
  • No custom fluent interfaces that render your application code almost completely useless outside of the context of the web framework you’re using

What you are seeing in the code above:

  • Concrete classes that are suffixed with “Endpoint” or “Endpoints.”  This is an out of the box naming convention in FubuMVC that marks these classes as being Action’s.
  • Public methods that take in 0 or 1 inputs and return a single “resource” model (they can be void methods too).
  • Route patterns are derived from the method names and properties of the input model — more on this in a later post because this one’s already too long.

 

One Model In, One Model Out and Automatic Content Negotiation

As a direct reaction to ASP.Net MVC, the overriding philosophy from the very beginning was to make the code we wrote be as clean and terse as possible with as little coupling from the application to the framework code as possible.  We also believed very strongly in object composition in contrast to most of the frameworks of the time that required inheritance models.

To meet this goal, our core design idea from the beginning was the one model in, one model out principle.  By and large, most endpoints should be built by declaring an input model of everything the action needs to perform its work and returning the resource or response model object.  The framework itself would do most of the repetitive work of reading the HTTP request and writing things out to the HTTP response for you so that you can concentrate on only the responsibilities that are really different between actions.

At runtime, FubuMVC is executing content negotiation (conneg) to read the declared inputs (see the NameModel class above and how it’s used) from the HTTP request with a typical combination of model binding or deserialization, calling the action methods with the right input, and then rendering the resource (like HomeModel in the HomeEndpoint.Index() method) again with content negotiation.  As of FubuMVC 1.0, rendering views are integrated into the normal content negotiation infrastructure (and that ladies and gentlemen, was a huge win for our internals).    Exactly what content negotiation can read and write is largely determined by OOTB conventions.  For example:

  • If a method returns a string, then we write that string with the content-type of “text/plain”
  • If an action method returns a resource model, we try to “attach” a view that renders that very resource model type
  • In the absence of any other reader/writer policies, FubuMVC attaches Json and Xml support automatically with model binding for content-type=”application/x-www-form-urlencoded” requests

The automatic content negotiation conventions largely means that FubuMVC action methods just don’t have to be concerned about the details of how the response is going to be written out.

View resolution is done conventionally as well.  The easiest, simplest thing to do is to simply make your strongly typed Spark or Razor view render the resource model type (the return type) of an action method and FubuMVC will automatically apply that view to the matching action.  I definitely believe that this was an improvement over the ASP.Net MVC ViewResult mechanism and some other frameworks *cough* NancyFx *cough* adapted this idea after us.

The huge advantage of the one model in, one model out was that your action methods became very clean and completely decoupled from the framework.  The pattern was specifically designed to make unit testing action methods easy, and by and large I feel like we met that goal.  It’s also been possible to reuse FubuMVC endpoint code in contexts outside of a web request because there is no coupling to FubuMVC itself in most of the action methods, and I think that’s been a big win from time to time.  Try to do that with Web API, ASP.Net MVC, or a Sinatra-flavored framework like NancyFx!

The downside was the times when you really did need to exert more fine grained control over HTTP requests and responses.  While you could always happily take in constructor dependencies to read and write to the raw HTTP request/response, this wasn’t all that obvious.

 

The Russian Doll Behavioral Model

I’ve regretted the name “FubuMVC” almost from the beginning because we weren’t really a Model 2 MVC framework.  Our “Action” methods just perform some work within an HTTP request, but don’t really act as logical “Controller’s.”  It was also perfectly possible to build endpoints without Action methods and other endpoints that used multiple Action methods.

The core of FubuMVC’s runtime was the Russian Doll “Behavior” model I described way back in 2011 — in which action methods are called inside of a pre-built Behavior in the middle of a chain.  For example, our HomeEndpoint.Index() action above has a chain of nested behaviors in real life something like:

  1. AuthenticationBehavior
  2. InputBehavior — does content negotiation on the request to build the HomeModel input
  3. ActionCall –> HomeEndpoint.Index() — executes the action method with the input read by conneg and stores the output resource for later
  4. OutputBehavior — does conneg on the resource and “accepts” header to write the HTTP response accordingly

FubuMVC heavily uses additional Behavior’s for cross cutting concerns like authorization, validation, caching, and instrumentation that can be added into a chain of behavior to compose an HTTP pipeline.  Every web framework worth its salt has some kind of model like this, but FubuMVC took it farther by standardizing on a single abstraction for behaviors (everything is just a behavior) and exposing a model that allowed you to customize the chain of behaviors by either convention or explicitly on a single endpoint/route.

In my opinion, the Behavior model gave FubuMVC far more modularity, extensibility, and composability than our peers.  I would go so far as to say that this concept has been validated by the sheer number of other frameworks like Microsoft’s WebAPI that have adopted some form of this pattern.

 

Clean, terse “magical” code versus explicit code

The downside to the behavior model, and especially FubuMVC’s conventional construction of the nested Behaviors is the “magical” aspect.  Because the framework itself is doing so much more work for you, there isn’t a blob of explicit code in one place that tells a developer everything that’s happening in an HTTP request.  In retrospect, even though I personally wanna write the tightest, most concise code possible and avoid repetitive code, other developers are much happier writing and reading code that’s much more explicit — even when that requires them to write much more repetitive code.  It turns out that repetitive code ceremony is not a bad thing to a large number of developers. 

Other developers hated the way that FubuMVC doesn’t really do much to lead you to what to do next or make the framework capabilities discoverable because so much of the code was meant to be driven by FubuMVC conventions based on what your code looked like rather than you writing explicit code against FubuMVC API’s with Intellisense there to guide you along the way.  And yes, I’m fully cognizant that I just make an argument in favor of a Sinatra style fluent interface like NancyFx’s.  I know full well that many developers considered NancyFx much easier to learn than FubuMVC because of Nancy’s better discoverability.

We did offset the “magical” problem with diagnostics that I’ll discuss at a later time, but I think that the “magical” aspect of FubuMVC scared a lot of potential users away in retrospect.  If I had it to do over again, I think I would have pushed to standardize and describe our built in conventions much earlier than we did — but that’s another blog post altogether.

 

What I wanted to do in FubuMVC 2.0 

I had no intention of adopting a programming model more like Sinatra or Web API where you write more explicit code.  My feeling is that there is room in the world for more than one basic approach, so for 2.0, I wanted to double down on the “one model in, one model out” approach by extending more conventions for finer grained control over the HTTP request & response without losing the benefits.  Things like:

  • More built in model binding conventions to attach Cookie values, system clock values, IPrincipal’s, and whatever else I could think of into the OOTB model binding.
  • Built in conventions for writing header, response codes, and cookie values from resource model values to maintain the one model in, one model out motif while still allowing for more powerful HTTP API capabilities
  • We did change the content negotiation defaults to make views “additive” to Json/Xml endpoints, so that any endpoint that renders a view for accept=”text/html” can also return Json or Xml by default
  • We made it a little easier to replace the built in Json/Xml serialization
  • We did streamline the content negotiation internals to make customization smoother
  • Add new built in conventions to attach custom content negotiation readers and writers to the appropriate input and resource types

 

10 thoughts on “FubuMVC Lessons Learned — Magic Conventions, Russian Dolls, and Ceremonial Code

  1. Sure the asp.net MVC encourages repetitive code because it makes it difficult to define and inject conventional logic. However, the conventional logic in fubu has to exist *somewhere* so why not put it somewhere the developer can see and interact with? It’s not repetitive if it only exists once, right?

  2. One thing that FubuMVC missed (IMHO) was a mechanism to opt-out from very specific conventions that surely didn’t fit our needs.
    Many times I issued a PR (which I reckon at times they were not of the highest quality) but while waiting for feedback or for someone of the maintainers to take care of, I just went and hacked a temporary workaround for it (shame on me, I know).

    One of these problematic conventions come from FubuValidation, where “number” types are tagged with a numeric validation rule at client side.
    That’s nice and all, but one thing that the library missed were nullable “numbers” like “int?”, that meant for us that these input fields weren’t meant to be checked if they had an empty value.
    https://github.com/DarthFubuMVC/fubuvalidation/blob/master/src/FubuMVC.Validation/content/scripts/fubuvalidation.rules.js#L219

    And that convention is deep inside the library set in stone (a mix of services and html conventions), of course, I could go and do some juggling with services and hacking with javascript code, but that’s not the point, I think some sort of defined model/graph for conventions was needed to regulate their usage.

    Don’t get me wrong, I think you guys truly delivered a framework that was one on its kind (for good).

    Finally, FubuValidation is the way to go for validation business in the .Net/OO world, and I hope the ideas behind it inspire other frameworks to nailed it as this wonderful library does.

    1. @Jaime,

      Maybe the validation conventions didn’t but every other convention in FubuMVC was overridable on a case by case basis. I think you’re a little off base here. Html conventions were easy to override, and core services could be replaced at will.

      My recollection is that your PR’s were frequently missing tests and weren’t really finished and ready to pull.

      1. Hi Jeremy,

        Sorry if I sounded as if this was the case for all the conventions in place, that’s definitely not true.

        About the PR’s, I have come a long way from my days in .Net and I agree with you that present Jaime laughs at past Jaime in certain things like the lack of testing in some of my PR’s.

  3. Hi Jeremy,

    I know you poured enormous amounts of your time and energy into this project. I want to thank you for your effort, and I also want to say that I’m sorry you have decided to throw in the towel on FubuMVC and .NET OSS in general. This community, however ungrateful it may seem, will absolutely miss your contributions.

    I do want to share my experience with FubuMVC. I came to the project in August 2013 as a frustrated ASP.NET MVC user. The philosophy behind FubuMVC, which was written up in several places, really resonated with me. I’m definitely *not* a developer who enjoys ceremony or repetitive code, and I absolutely *do* want to focus on what’s really different between actions.

    That said, I found myself unable to get off the ground with FubuMVC. Part of this was certainly my own lack of sophistication as a developer, and part of it was simply a lack of perseverance, but the lack of good documentation was a major roadblock for me. Not only was it not clear which was the “real” documentation (GitHub? mvc.fubu-project.org? fubuworld.com?), but whichever one I tried, it just wasn’t complete enough for me to get my head around what I needed to know. After struggling for a while, I decided to give up and take another look at NancyFx. From a theoretical standpoint, it didn’t resonate with me as strongly as FubuMVC, but thanks to its excellent documentation, I was able to quickly get up to speed.

    I know you and the other contributors were always aware that documentation was an issue and worked very hard to try to catch up, so I’m sure nothing I’m saying here is a revelation. I just want you to know that there are people out there who have no argument with the core tenets of FubuMVC, they just need a fair amount of guidance to be able to get started and feel productive right away with a new framework. I also have one minor suggestion: please put all your documentation, however incomplete it may be, in one place!

    I really hope FubuMVC survives, even if you are no longer its fearless leader. If nothing else, it has and will continue to be influential.

    1. Everything good is on FubuWorld.com. At some point we were gonna get around to just having all the old *.fubu-project.org links forward on to FubuWorld.

      https://groups.google.com/forum/#!searchin/fubumvc-devel/fubuworld.com/fubumvc-devel/OcA-irEzB9o/_SkgWnwWjoAJ

      In a typical me kind of way, I spent a lot of effort writing FubuDocs (http://fubuworld.com/fubudocs) to make documentation writing easier instead of writing documentation content. I think the effort paid off, but we got FubuDocs to a usable state not long before we all gave up and started moving on.

  4. I’ve known about FubuMVC for a while. At the time I wasn’t doing web based work, so I had no experience with ASP.NET MVC. All I heard was that FubuMVC was better, but very little reason on why. All I heard was Microsoft sucks, we are better and with no comparisons to back that up. It put me off. When I tried to look at FubuMVC lack of documentation made me just give up. I learned more about Fubu from this post than I think I ever have from anything else.

    Same goes for Structuremap, we use it in current MVC app. But when I tried looking at documentation it was outdated and sometimes unclear. I like Structuremap and think it’s great, but I found it hard to figure out what was the current and best way to use it.

Leave a comment