FubuMVC Behavior Chains - the BMVC Pattern
For the last several months at work, we have been gradually migrating our production website to use the .NET open source FubuMVC web framework. Before committing to a whole new infrastructure for our site, some brilliant talent on our team decided to improve our experience on the ASP.NET MVC framework by integrating in one of FubuMVC’s most compelling features, behavior chains. Bob Pace originally blogged about the process in a two part blog post series (part 1 and part 2); since then, he has created a packaged solution called MvcToFubu which he just recently started another blog post series about.
Behavior chains are by far my favorite feature of FubuMVC. I believe they single-handedly revolutionized our use of the MVC pattern in a web environment. In fact, I like to think of it as a whole new pattern, the BMVC Pattern (I was told by a coworker that the ‘B’ should stand for “Better”, and while that’s true, I’ll stick with “Behaviors”). No, I’m not sincerely trying to coin yet another design or architectural pattern buzzword. However, I am most sincerely not joking when I say behavior chains add a whole new dimension to how I design web architectures. I find myself creating composable pipelines of cohesive, testable components in lieu of monolithic controllers and actions.
What are Behavior Chains?
Have you ever looked at your controller actions and thought “Man, there’s a lot going on here!” (validation, authorization, transactions, ORM setup, logging, caching, etc.)? If you haven’t, I urge you to take a conscious look at your controller actions and try to identify code that doesn’t directly relate to the purpose of the page (or sub-view of the page). Shouldn’t there be a way to move some of this secondary infrastructure code out of our controller actions and have it be applied conventionally to the correct actions/routes? Yes, and this is exactly what Behavior Chains are meant to accomplish, the ability to create composable pipelines of reusable behavior nodes for each route in your site.
Behavior chains can be built up in any order you want for each route via Conventions and Policies, which are extension points that allow you to inspect and modify the entire Behavior Graph that was built by FubuMVC during startup bootstrapping. The behavior graph is the modifiable collection of all behavior chains in the site, initially built with just a few default behavior nodes for each route. A typical behavior chain by default usually has an ActionCall node (representing the call to a controller action) and an Output node (often a tie-in to a view rendering engine, but can also be JSON or anything else). Other behavior nodes can be sprinkled into the pipeline in any order in front of or in between the ActionCall and the Output nodes. As mentioned before via the use of Conventions and Policies, it is common practice to filter your list of behavior chains using LINQ extension methods and then add a specific behavior node into the matching chains at a specific location in the chain. This truly elevates coding to conventions to a new level; some of the filtering criteria regarding controller actions that you may come up with could be as follows:
- Action method names that contain a certain word.
- Actions methods that are located in a particular controller or namespace.
- Action method signatures that contain an input/output model that implements a specific interface.
- Action method signatures that contain an input/output model that contains a property of a certain type or have a particular C# attribute applied to it.
- So on, so forth…whatever you can dream up!
One more aspect of behavior chains that is important to point out is the logistics in how behavior nodes are chained together. The chains are not merely a linked-list of behavior nodes that are traversed linearly. Instead the makeup of the chains is better conceptualized to be an outer behavior node wrapping around an inner behavior node, which is then itself an outer node wrapped around another inner node, and so on. In fact, the FubuMVC developers like to refer to this chaining architecture as the Onion Layer Model or the Russian Doll Model (conjuring up a mental image of the nesting Russian doll toy called a matryoshka doll or a babushka doll). When an outer behavior node has finished executing it’s logic, it can then optionally execute it’s inner behavior node logic (or stop the behavior chain entirely in order to return early with a response, an error, or even transfer to a different route and behavior chain). One of the key advantages that this wrapping architecture can afford is the ability for an outer behavior to run additional logic or clean up code after all inner behavior nodes have completed (i.e., transactions can be committed, connections can be closed, sessions can be disposed, etc.).
How does this change the way I code my controllers?
I find that my controller actions are much thinner and more focused on the base case and the successful code path. All the exceptional code paths and edge cases have been identified and deal with by the other behavior nodes in the chain, prior to my action call being invoked. My action call can now focus on the essential logic that differentiate this route from others. That essential logic doesn’t get lost anymore in the necessary infrastructure code that should be a secondary concern. And all of this logic can be broken up into cohesive, reusable components within a flexible request/response pipeline that can be built up automatically by coding to whatever conventions you deem beneficial. Behavior chains really do add a whole new first-class citizen to the MVC design pattern. Do yourself a favor and check out the MvcToFubu solution mentioned at the top of this post, and bring some FubuMVC behavior chain goodness to your ASP.NET MVC website. I think you might be as impressed as we were and yearn for the other killer features of FubuMVC as well.