Sunday, February 25, 2007

Navigation Controller Pattern

When people use the term "website navigation", they usually refer to one of the two different things. First is a set of hyperlinks displayed on a web page that users can click in order to get to a specific part of the website. These links may be presented as a drop-down menu, tree-view, or a bread crumb trail. Such navigation controls are easy to incorporate, especially with ASP.Net 2.0 where they all bind to a common data source.

Another meaning of the term is related to page flow: a sequence of pages user needs to go through in order to accomplish some process. Ordering a book from Amazon.com is a simple example: from the shopping cart screen, user has to go to the shipping details page, then billing details, review order summary, order receipt, and, finally, suggested items list. An example of a complex flow is creating a will on LegalZoom.com. In both cases, entire sequence is controlled by the application -- user cannot bypass or reorder steps. Thus, page flow is part of the application's business logic while navigation controls are part of its user interface.

Traditionally, page flow is implemented either in the code-behind class or page presenter (if the application follows the MVP pattern) as a sprinkle of Response.Redirect() calls. Although this approach is straightforward for developers, it has an important limitation: page flow logic is inseparable from the rest of the application logic. Unless the system is fully documented (and we know how often that happens, right?), it is impossible to answer a question "How many pages lead to Foo.aspx" without scanning the source code. Consequently, application becomes difficult to maintain: simple business request to change default page flow becomes a challenge for a developer, especially if he or she isn't intricately familiar with the system, and requires extensive regression testing.

Since I mentioned the subject of technical documentation, one reason it’s so unpopular is because any documentation almost immediately becomes outdated. Imagine how much easier it would have been if we had a “live” system diagram. But that is a subject for a different post…

Navigation Controller design pattern provides a better alternative. As it name implies, entire page flow navigation logic is abstracted to a new class. At the core of navigation controller are two types of page transitions: default and named. Default transition will transfer user to the next web page within the flow, while named transition represents an alternative navigation route. For instance, a page that displays credit card details will transition by default to the order summary page:

IPageFlow controller = this.NavigationController;
controller.Next();

However, if user decided to add new credit card info, control should be redirected to that page instead:

IPageFlow controller = this.NavigationController;
controller.Navigate(TransitionNames.AddNewCreditCard);

Note that the string Navigate method accepts isn’t the URL of the page, but a generic value that represents the transition. The logic within navigation controller maps transition name to actual page URL.

Essentially, navigation controller is a state machine where each state is associated with a particular web page. As all state machines, it needs to have a start and end states, and maintain current state information. From the web application point of view, every user session requires a dedicated instance of the controller class.

As we know, nothing prevents a user from typing the URL directly in their browser window or clicking “Back” button. Both these actions can potentially break the intended page flow, so another function of navigation controller is to “keep” user in:

protected override void OnLoad(EventArgs e)
{
IPageFlow controller = this.NavigationController;
controller.RedirectToCurrentPage();
base.OnLoad(e);
}

Microsoft Patterns and Practices group recently released initial version of its Web Client Software Factory. The factory includes, among other things, Page Flow Application Block, which is a versatile navigation controller. As all PnP application blocks, it is build using provider model, meaning that there may be different concrete implementations of the controller. The team actually chose Windows Workflow Foundation (WF) for their implementation. In WF, page flow is represented by a state machine workflow and can be edited in the visual designer inside VS 2005. Because WF has its own persistence model, page flow instances need not be stored in user session state and can be paused and resumed as necessary. For more information, please visit Web Client Software Factory wiki on CodePlex.

No comments: