The move from the development of traditional web applications to rich Internet applications often requires a shift in mindset because an RIA client can be entirely stateful.
Before the mainstream adoption of AJAX, web application developers were all too familiar with the stateless request/response approach. Moving on from somewhat crude approaches to maintain state on the client, such as continually stuffing data into an HTTP request as the client and the server exchange pages, or using URL encoding or cookies to keep some limited state information on the client, it became possible to twist the use of the HTTP request/response model to behave like a rich Internet application. No matter the approach used, the typical application developer's aim was to maintain state on the server, and make it available to the client.
With Flex RIAs, however, web application developers can break free from the shackles of HTTP request and response. You no longer have to concoct solutions for the stateless client because the client is indeed stateful. If you have been building desktop or rich-client applications with technologies such as Swing or AWT you'll shrug your shoulders right now because all of this will sound familiar—it is.
Many developers are familiar with the concept of MVC or Model/View/Control in application development, and wonder where state fits into this discussion. Quite simply, the state is the model. As obvious as this may sound, it presents additional common challenges to solve in the implementation of the model in an RIA.
First, resist the opportunity to scatter state all over your application as strings, numbers, Booleans, and all manner of other primitive objects. It is not my purpose here to promote best practice object-oriented (OO) development. Indeed, accepting the benefits and practicing the principles of OO are almost prerequisites to understanding this article.
However, we strongly advocate that the client hold the data—as state, model, or whatever you want to call it—as objects that have semantic meaning. What is semantic meaning in this context? It means, for example, that if you sell fly-fishing flies online, your object model should include "flies," "patterns," "hooks," and "hackles." If you sell mortgages, your object model should include "products," "rates," "repayments," and "terms." If you sell maps, your object model likely includes "routes," "waypoints," "points of interest," and "gas stations."
One challenge is relatively unique to the stateful client of a rich Internet application: The state on the client must reflect the state held on the other side of the wire, in the server middleware. Let's examine this design problem by considering a similar challenge that exists entirely on the server side.
In server-side application development, the state often exists within two tiers of an application architecture, specifically the business tier and the integration tier. The integration tier usually contains databases, mainframes, message queues, ERP solutions, CRM systems, and other enterprise information systems that hold data on the objects and process particular to a business domain. In enterprise application development, these systems are typically aggregated and presented to users through a middleware tier, also called the business tier.
Keeping state consistent across these two tiers is a challenge. First of all, the representation of data in these tiers may be dramatically different. For instance, the entities, relationships, views, and queries that exist in a relational database solution must be mapped from a relational model in a database to an object model in the middleware business tier. You can use technologies such as Enterprise JavaBeans, Hibernate, and ADO.NET to map data across these architectural tiers.
Irrespective of the solution chosen, the application developer assumes a great deal of responsibility in ensuring the consistency of state across tiers. As soon as you keep the same information in two places, there is a risk that changes made in one place are not reflected in the other, or that changes in one place overwrite changes made in the other. Suddenly, the developer must be concerned with concurrency, locking, transactions, rollback, and other approaches for ensuring that the model is consistent across the architecture.
As RIA developers, you are distanced from the complexities between the business and integration tier; instead, you are more concerned with ensuring ensure that the model in the business tier is consistent with the model in the presentation tier.
You need to ensure that you have a consistent object model on the client and server, and minimize the effort required to keep these object models synchronized. RIA developers use the Data Transfer Object pattern to do this.
First, consider how to best model the client data. Whenever you want to represent the value of things on the client, you can do worse than to create classes or objects that represent the values of those things. For instance, in Cairngorm Store, one thing that you must represent is the products in the store. Each product has a number of attributes: name, description, price, an associated image for the detailed view, and an associated thumbnail image for the catalog view. These attributes can together form a Product class.
These classes, or value objects, play a dual role in a rich Internet application. In addition to keeping the value of an item on the client, they are useful structures for exchanging or transferring data between different tiers in an application.
In server-side development, consider a strategy in which you query a database using SQL, receive a result set, and then transform that result set into a collection of value objects representing the things you have pulled from the database. By passing collections of these value objects between application tiers, you insulate each tier of the application from the underlying implementation of its neighboring tiers. The middleware need not care whether these value objects were created from an SQL query, stored procedure call, message queue, or web service call. By reusing these value objects, you decouple architecture by transferring data between tiers as objects with business meaning ("Products," "Fish," "Maps") rather than technical meaning ("RecordSets," "ResultSets," "Data Sets"). This has led developers to call the Value Object pattern the Data Transfer Object pattern instead.
We conceived Cairngorm before the term data transfer object (DTO) became popular, so we called—and continue to call—the business objects value objects or VOs.
For example, take a look at an example VO from the Cairngorm Store application:
package com.adobe.cairngorm.samples.store.vo
{
[RemoteClass(alias="com.adobe.cairngorm.samples.store.vo.ProductVO")]
public class ProductVO implements IValueObject, Comparable
{
public var id : Number;
public var name : String;
public var description : String;
public var price : Number;
public var image : String;
public var thumbnail : String;
…
}
}
This is straightforward. The ProductVO is nothing more than a collection of attributes that describe what makes a Product a product. Whenever you keep state on the client about Products, keep it as instances of the ProductVO class. If the application must know what the "currently selected product" is, store that information on the client as a ProductVO instance. If the application needs to retain a list of the products in a customer's shopping cart, store that information on the client as a list of ProductVO instances.
Furthermore, if you have to transfer products from the server (and we do, but more on that later) or if you ever have to transfer products back to the server, transfer that data back and forth as value objects. Others might say that you're using data transfer objects, because you are. They're the same thing, remember?