Chapter 2: Architecture
The Mileage Stats Reference Implementation (Mileage Stats) is a cross-browser, ASP.NET Model-View-Controller (MVC) application that takes advantage of the features of modern browsers. The application offers two types of user experiences:
- A traditional website experience. In this approach, a form post and page reload are executed each time a button or hyperlink is clicked.
- A rich website experience. In this approach, the initial page is loaded once, and server requests are only made when new data is required or updated. In addition to other user-friendly features, the lack of a full-page reload enables the animation of client-side state changes.
The rich website approach provides a superior experience for the user, as the application feels more responsive and more like a desktop application. However, because some users do not have scripting enabled or available on their user agent (web browser or accessibility tool, such as a screen reader), which is necessary for the partial-page reloads, we must also support the traditional website experience.
In the traditional approach, the ASP.NET MVC controllers are responsible for acquiring data and for returning a built-up view that consists of HTML structure and data. In the case of the rich website experience, we perform asynchronous data requests and the controller returns only data. The client then renders the data in the user interface (UI) without reloading the whole page.
Supporting both these experiences introduces complexity that requires careful planning on both the client and server sides to ensure that the application is responsive, maintainable, has a clean separation of concerns, and is testable.
You should determine early in the design phase which experience the user should expect in each browser and browser version the application will support. If you choose to support older browsers, you may limit your technology choices and affect the run-time experience of the application. Shims and polyfills, such as those that provide HTML5 support, are available for adding support for some technologies in older browsers, but these come at the cost of additional dependencies (see "Further Reading" at the end of the chapter to learn more about shims and polyfill solutions). Making decisions early on about which technologies you will need to support allows you to establish realistic expectations for users and project stakeholders.
This chapter provides a high-level map of the Mileage Stats client-side architecture, and is divided into five areas of discussion: structure, modularity, communication, navigation, and data.
- Structure refers to client-side HTML structure and manipulation. It is represented below as the Template.
- Navigation explains how to manage user gestures and coordinate animations. It is represented below as Navigation and the Layout Manager.
- Data provides guidance for client-side data requests and data caching. It is represented below as the Data Manager.
In this chapter you will learn:
- Options and strategies for getting the right HTML to the client.
- The advantages of modular code, and techniques for using jQuery UI widgets.
- How the Publish/Subscribe (pub/sub) pattern can be used for loosely coupled communication.
- How to solve browser history and back-button problems when the site doesn't perform full-page reloads.
- How a loosely coupled data layer can simplify caching for client-side data requests.
- How the Mileage Stats team solved a number of challenges related to structure, modularity, communication, navigation, and data.
To provide an engaging, responsive, and interactive experience, the application needs to manage client-side structure changes without performing full-page reloads. This requires client-side loading, creation, and replacement of HTML fragments or pages.
The data can be a single object or an array of objects. jQuery templates separate structure and data, making the application easier to code, test, and maintain.
If you use ASP.NET MVC or ASP.NET Web Forms, you can use the rendering engine to dynamically create or modify the jQuery template while it's being rendered. Mileage Stats uses this capability to inject URLs and data- (pronounced "data dash") attributes into the templates at render time.
Mileage Stats loads all jQuery templates as part of the initial page load. Preloading templates simplifies the client-side application and provides much faster client-side rendering than on-demand loading of templates.
For more information on the jQuery Template plug-in and authoring templates, see "jQuery Templates" in the "Further Reading" section. For more information on jQuery templates in Mileage Stats, see Chapter 7, "Manipulating Client-Side HTML."
- UI. Includes these jQuery UI widgets: vehicle, vehicle list, information pane, vehicle details, vehicle fill ups, vehicle reminders, registration, statistics, summary, status, header, and charts.
The jQuery widgets that compose the Mileage Stats Dashboard are pictured in the image below. The complexity of the application demonstrates the need for modularization. By breaking the implementation into discrete, loosely coupled objects, the client-side code is much easier to understand, author, maintain, test, and debug.
- Status widget. Provides management and display of user notification messages.
- Summary widget. Acts as a container, managing its child registration, statistics, and reminders widgets.
- Statistics widget. Displays summary statistics for all vehicles.
- Reminders widget. Lists overdue and upcoming maintenance reminders. Manages the action of clicking on a reminder.
- Layout manager widget. Services navigation requests and coordinates UI layout changes.
- Vehicle list widget. Displays the vehicle tiles in a one-column or two-column listing. Invokes the child widget animation when required and controls when child widgets are displayed in expanded or contracted view.
- Tile widget. Provides drag-and-drop capability for the child vehicle widget.
- Vehicle widget. Displays vehicle information in expanded or contracted view. Manages the actions of each button.
- Header widget. Provides top-level navigation and user name display. Manages actions when a hyperlink in the header is clicked.
For more information on modularity in Mileage Stats, see Chapter 5, "Modularity." For more information on jQuery UI widgets see Chapter 3, "jQuery UI Widgets" and Chapter 5, "Modularity." For more information on pinned sites, see Chapter 10, "Application Notifications."
If not carefully planned, communication between objects can lead to tight coupling and undesirable dependencies. Mileage Stats objects either communicate directly with one another, or loosely by using a publish and subscribe pattern (pub/sub).
Direct widget communication is typically reserved for high-level widgets controlling lower-level widgets, such as when the layout manager tells a widget to hide or show itself.
Pub/sub is a messaging pattern that enables loose communication between publishers and subscribers. When a message is published, zero or more subscribers will be notified. A pub/sub object manages communication, relieving the publishers and subscribers of needing direct knowledge of one another. Pub/sub messages are individually defined and can optionally contain a payload.
The pub/sub pattern provides clean separation between the object invoking the action and the object that handles the action. This separation allows the publisher and subscriber's internal implementations to evolve without affecting each other.
Mileage Stats has its own pub/sub implementation that provides for loose communication. For example, the Status widget subscribes to the status message. The status message has a payload that contains message, type, duration, and priority values. Publishers of the status message provide these values when publishing this message.
Mileage Stats widgets have publish and subscribe functions passed into their options object during construction to decouple them from the pub/sub implementation.
For more information about the pub/sub implementation in Mileage Stats, see Chapter 8, "Communication."
Rich client-side web applications like Mileage Stats do not perform full-page reloads each time a button or hyperlink is clicked. Instead, client-side application code handles these events.
The jQuery BBQ plug-in is responsible for providing address bar URL changes. Changing the address bar URL performs two functions. First, it allows users to bookmark addresses so that they can return directly to a particular application state. This is known as deep linking. Second, it enables the browser history and back button to perform as the user expects.
The Mileage Stats layout manager is a widget that works in conjunction with the BBQ plug-in to service navigation requests. It subscribes to the BBQ plug-in hashchange event, and initiates layout changes based on address bar URL changes.
Along with hiding and showing UI elements, the layout manager is also responsible for initiating UI animations during navigation. The layout manager does not perform the animation, but sequentially calls methods on one or more lower-level widgets, resulting in an engaging UI transition.
As part of the layout manager's top-level widget responsibilities, it subscribes to several pub/sub messages and invokes lower-level widget data refresh methods when those messages are published. For more information about navigation and the layout manager, see Chapter 9, "Navigation."
When designing your client-side data architecture, several key decisions will impact application performance, maintainability, and browser support. Will data requests flow through a central object or will objects make direct calls to the server? Will data be cached, and if so, how much? Will data be prefetched, and if so, how much? Answers to these questions will vary based on your application's specific requirements.
Mileage Stats prefetches chart data during the initial page load, enabling instant application response when the user navigates to the Charts page. Whenever data is returned from the server, it's cached. This can make the application more scalable because repeated requests to the server for the same data are no longer necessary, requiring less server processing per user.
For in-depth coverage of data management and caching, see Chapter 6, "Client Data Management and Caching."
The use of these libraries does create more dependencies, but this usually has a positive impact on the overall design of the application by making it easier to control the separation of concerns that is so important to complex user interfaces. If your UI has a number of screens made up of multiple regions and includes complex interaction patterns, and having additional dependencies is acceptable to you, you should consider applying these patterns and using these libraries. A design the Project Silk team is interested in investigating in the future involves the use of jQuery UI Widgets that can be data bound within an MVVM implementation such as Knockout.js.
Building a rich web application that reduces the number of full-page loads, includes animations, and is responsible for updating the UI dynamically requires a thoughtful approach to managing structure, modularity, communication, navigation, and data. This chapter provided a high-level view of the Mileage Stats client-side application architecture. The following image shows the client-side objects and their implementation mapped to libraries or frameworks.
For more information on jQuery templates in Mileage Stats, see Chapter 7, "Manipulating Client-Side HTML."
For more information on pinned sites, see Chapter 10, "Application Notifications."
For more information on modularity in Mileage Stats, see Chapter 5, "Modularity."
For more information about the pub/sub implementation in Mileage Stats, see Chapter 8, "Communication."
For more information about navigation and the layout manager, see Chapter 9, "Navigation."
For more information about data management and caching, see Chapter 6, "Client Data Management and Caching."
For more information about the libraries and guidelines discussed in this chapter, see the following:
"jQuery BBQ: Back Button & Query Library" on Ben Alman's blog:
"Filling the HTML5 Gaps with Polyfills and Shims" from Rey Bango's MIX11 session:
"Making HTML5 and CSS3 work with polyfills and shims" by Rey Bango on .net magazine: