Lately, its been raining M-V-VM (Model-View-ViewModel) in the SL and WPF world, and rightfully so because M-V-VM to me is the best pattern for application-development given the features in SL and WPF. Now, in this post I'm not going to introduce or detail the pattern itself, read this article by Josh Smith for a great intro, rather I'll go over what nRoute (download here) brings to the party as far as M-V-VM is concerned. And this is significant because nRoute was specifically, but not exclusively, designed for use with M-V-VM.
Separation of Concerns
From a larger perspective, M-V-VM is based on a simple principle of "separation of concerns" - in this case between the consumable View and the View's State and Behaviour which is represented by the ViewModel. However, it is important to appreciate that the ViewModel is not a dumb conductor, rather it is a contextually and state-fully rich orchestrator (which inturn makes the View as "logic-free" as possible). This separation also plays into enhancing the Developer-Designer workflow Microsoft has been touting for sometime.
Further, in my previous post about the demo app I explicitly tried to highlight how nRoute helps in another type of separation of concerns - between the infrastructure faculties and the actual actionable/usable content. Using Urls as a abstraction over content, nRoute dynamically binds together the infrastructure and content into a consumable application. The critical benefit being that infrastructure and the usable/actionable content can be clinically separated and evolved independently of each other.
This whole range of separation of concerns, with the traditional n-tier and domain-model helps create a flexible application and infrastructure - perhaps more complex, but one that will stand the stress-of-change much better.
Central to nRoute is the routing engine, which is a direct port of the engine found in ASP.NET 3.5sp1 or MVC. It plays the role of a mediator that uniquely identifies resources using Urls, which are used for both composition and navigation purposes. In addition to a "resource locator" role the mediator also has another important role, which is that it allows for loosely-coupled dependencies amongst resources. When I say dependencies amongst, it can either be of composition nature like parent-child windows, or it can be of linkage nature like in wizards screens - both of which are discussed below. Also appreciate that because the mediator in nRoute uses string based Urls to resolve dependencies, your resources can evolve independently of each other akin to say HTML pages on a Web server.
Often people find the use of strings as locators a bit too loose of an abstraction, however Urls in nRoute are not magic-strings rather they have to be formally registered and properly formatted too. And equally the fact is that Urls are perhaps the most successful and all-encompassing form of resource locators in use. Further like MVC et al., Urls in nRoute support tokenised fragments which makes for even easier and broader use semantics.
Increasingly the metaphor of a "Window" is taking a back seat to the metaphor of a "Page", because with the success of the Web-Page model the window metaphor easily breakdowns in the face of web-like fluent user experiences. And though, it is not technically hard to achieve composition in its simplest form, what has historically hurt is the lack of a proper/built-in composition model. Further the complexity and clumsiness of solutions like Regions in Prism or WPF Frames hasn't helped, that is without even considering the locator, state-management, and navigation type issues the page metaphor brings.
And in this scenario, nRoute really shines because it brings a simple and well-understood model for composition using Urls, along with inherent support for state management and navigation features. The composition model in nRoute is akin to IFrames in HTML, and as suggested it makes use of so-called "container controls" (or simply containers) which by design exhibit different state-management behaviours (like Back-Forward History). Build into nRoute are four such container, plus the container model is fully customizable/extensible to accommodate custom needs. Now, what is profound here is that your content doesn't need to be aware of the container or vice-versa, because nRoute makes the composition magic happen without direct dependencies.
Further, like HTML pages/IFrames you can have one container within another, which allows for Master Pages or Parent-Child Pages type of use models. Alternatively, you can place two or more containers next to each other (each container individually addressable) to get something like the antiquated FRAMES model in HTML. In all, with the composition model in nRoute gives you a boat-load of flexibility coupled with a high degree of loose-coupling between the infrastructure and the content.
With composition, navigation has always been the bane - but by using Urls it becomes both simple and easy to effect. Further, nRoute features an extensive range of behaviours that extend navigation to almost all control's common events, and accompanying that is a simple NavigationServices API. And depending on your preferences you can either drive the application-flow via code in the ViewModel or in the xaml based View. Additionally also supported is a concept-of an application-wide default container for navigation purposes, which comes into play in deep-linking scenarios.
Interlinked to navigation is state-management, which in nRoute is an opt-in feature driven by a simple interface called ISupportNavigation. Within a M-V-VM archetype, you can implement the said interface in the ViewModel, and equally use it for both saving and restoring state independent of the View. Furthermore, the ISupportNavigation interface helps formalize the passing of tokenized parameters (and optionally a name-value collection) in the course of navigation. The benefit of state-management vis-a-vis navigation is that the lifetime of any View is limited to it being active in a container - however the content state can be persisted independent of the View.
Because of the nature of M-V-VM separation of concerns, the command-model gains added prominence in achieving the View and the ViewModel interaction pattern. And nRoute features first-rate support for both creating and consuming ICommands - it has an expansive implementation of non-routed Commands and behaviours to enable its consumption. The ICommand implementations which I blogged about earlier, features a strongly-typed parameter, INotifyPropertyChanged notifications, enabling/disabling the command, and a bindable command parameter property. Further, like in the case of navigation, nRoute features an exhaustive range of behaviours for consuming ICommands, which can respond to almost every control's common events.
Actions in nRoute are Url addressable functionality, that execute off the UI thread - this is useful for things like triggering some downloads or enabling pub-sub type of communications. The model for constructing actions is relatively simple, it is based on an interface called IActionHandler and the consumption pattern like earlier is based on attachable behaviours or alternatively by the use of the ActionServices API. Further Actions support multi-casting to one or more globally registered handlers, and because of the loose-coupling it's a form of Weak Eventing using Urls. Also, you can direct an Action request to a specified handler which can be real handy sometimes, as described below.
In a way you can think of Actions in nRoute as mediated ICommands that can be addressed, in a loosely-coupled fashion, via Urls. And like ICommands, you can expose Actions in your ViewModel using a Delegate-Command type property (see the DelegateAction type), and on the View side you equally could bind to the Action property for use with behaviours. Further, like navigation Urls the actionable Urls also supports tokenized Url fragments and additively a request can be furnished with a name-value collection parameter for greater flexibility.
One of the obvious to-dos with M-V-VM is injecting the ViewModel into the View - and because in nRoute the Views are instantiated by the mediator it gives us the perfect opportunity and place to inject the ViewModel. There is a build-in attribute-based approach to this, which uses the wordy MapViewModelViewNavigation attribute to set the ViewModel as the DataContext of the View. However, you are not constrained to use this attribute, you could easily create your own attribute-based implementation that say uses a service locator to determine and inject the ViewModel. Further, if you know MVC then you'll appreciate how much can be done by creating a custom IRouteHandler, and in nRoute using the IRouteHandler extension point you could totally lay out a custom M-V-VM implementation atop nRoute.
One of the common tripping points with M-V-VM is the use of platform specific and/or task specific functionality like MessageBox or File Dialogs in the ViewModel. The solution for the same is wrapping the functionality in a platform/task agnostic way into a locatable service; however this begins to creep mightily into the territory of Service Locators such as Unity or NInject, which makes me rather uncomfortable. So, I am considering having a set of M-V-VM helper services that optionally plug-into your chosen implementation of a Service Locator (reverse Dependency Injection?).
In addition to the helper Services, a number of forthcoming features in nRoute will directly help with M-V-VM. Firstly, as described in my earlier post, we'll have strongly-typed INotifyPropertyChanged support using extension methods. Also introduced will be bindable data-triggers for SL, which can be used to trigger either Navigation, ICommands or Actions. And probably something like an Action that yields a response, with the response being consumable in the View.
In summary, I hope what is observable through the list of features mentioned above, is that nRoute is acutely geared towards realizing M-V-VM type of applications - ensuing a range of benefits probably described elsewhere.
Posted by Rishi on 13-May-09 7:44 AM, 30 Comments