This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with Active Server Pages

Architecting Your Web Applications
John Lam and Aaron Skonnard

Developing Internet-based apps means discarding many traditional concepts of client/server design. You have to pay attention to scalability and concurrency issues that never came into play before.
A friend confessed to us recently that he felt unsure about joining the ranks of the Web application industry. His strong C++/MFC skills gave him a sense of job security and self-worth that, in his mind, would be lost by becoming yet another HTML programmer. He was right to assume that simply adding HTML to his resume wouldn't make him invaluable overnight. Anyone can create an HTML page these days, right? Just ask your marketing department.
      Not everyone can create interactive Web applications that improve the profitability of your company. Even more difficult is creating Web applications that can scale over time without requiring a rewrite of a single line of code. Web application developers who understand how to build these complex systems are indeed invaluable in today's Web-driven market. If you're a Web application developer or trying to become one, it's imperative to understand the design considerations you'll be facing.
      In this article, we'll begin by reviewing the typical client/server design strategies used today and explain why they can't be adapted to the Web model. As you read this article, you'll learn how you can start thinking in terms of Web applications. In the process, you may have to set aside many of the design principles (and habits) you've learned over time because they simply don't work on the Web.
      In addition to covering the design limitations with the Web model, we'll define the concepts of a user session and session state in a Web application and how they can influence the overall performance and scalability of your system. We'll tackle one of the toughest design questions every Web application developer faces: where should I store session state?
      Next, we'll turn to a new Web application design pattern that we call the one-page Web application. While this innovative design pattern can be implemented on most browser flavors (if the developers are creative enough, that is), it becomes trivial with the new technologies built into Microsoft Internet Explorer 5.0. You'll see how XML along with new features in Internet Explorer 5.0 can help you achieve your Web application design goals.

Typical Client/Server Application Design
      Whether you come from a C++, MFC, or Visual Basic® background, you have probably dealt with client/server applications. If you've ever written a simple database front end or even a more complex multitier system, you've dealt with the client/server arena. Regardless of the project complexity, most client/server applications share certain design characteristics. First and foremost, typical client/server applications have a limited number of users. At design time, the maximum number of concurrent users that your system must support is given as a requirement. From that point on, most of the remaining design decisions are made around that assumption.
      Knowing how many people will be using your application concurrently can help tremendously as you make other design decisions along the way. But, as you design your system around the given requirement, you usually end up with a system that provides exactly that level of functionality and nothing more. If in six months the bigwigs in your company decide that the system should support twice as many users, more often than not you're back to the drawing board, or at least stuck rewriting substantial amounts of code.
      Most client/server applications also operate in a very controlled environment. Typically, developers are writing the code for both the client and the server. These same developers can usually dictate to a great extent such things as the required operating system, database, or any other system dependencies for both the client and server machines. Having this kind of control over your system's runtime environment reduces the complexity of the system and makes developers happy. However, arming developers with this kind of information at design time can lead to systems that are difficult to port to new environments.
      Another interesting client/server design concept is that of user connections. In most cases, when the user launches the client application, a connection is established with the server and maintained until the client application terminates. Because these connections are expensive and time-consuming, it's usually better to make the connection once and to share it among processes for the lifetime of the client application.
      Since the server maintains one connection per client, this model provides a simple mechanism for tracking user sessions. When the server picks up a new connection, it knows that a new user has begun using the system. As the user interacts with the system, the server can associate session-related data (state) with the user's connection or session ID. Once the client connection goes away, the user's session ends and the corresponding session state is released from server-allocated resources.
      Not all client/server applications are designed this way. These are just some of the typical client/server design characteristics that are common today. Chances are you're used to thinking about applications as we've just described.

Unlimited Users
      Unlike client/server applications, which have a limited number of users, Web applications have a potentially unlimited number of users. When you initially design the Web application, you may only expect 5,000 hits per month. But that 5,000 can turn into 500,000 virtually overnight. Most companies underestimate the potential of their applications during the design phase.
      In client/server scenarios, the company usually has control over the number of clients accessing the system along with the growth of the client base. This allows the company to plan for scaling the system. On the Web, you need to be prepared to scale from the very beginning. Having to completely redesign your Web application after three months on the market would be devastating to your company's strategic plans (and probably your career).
      In short, Web application developers must think in terms of unlimited users and, therefore, scalability. This doesn't mean that your Web application has to handle 500,000 hits per day right out of the gate. It simply means that your Web application needs to be designed with scalability in mind so that when you cross certain thresholds, you can increase throughput by adding additional hardware to your Web farm without changing any code. This is probably the most important thing to get right in your Web application design. We'll be discussing this concept in more detail throughout the rest of the article.

Browsers Galore
      Most Web applications, with the exception of intranet applications, also have very little control over their client's runtime environment. Users may try to use your Web application with various different browsers.
      Since you don't have any control over the client's runtime environment, you must decide early in the design phase which browsers your application will support. If you decide to support all of them, you'll be incredibly limited in client functionality. Most companies today decide to support certain versions of Internet Explorer and Netscape Navigator. This strategy makes development less complex and less restrictive. In this situation, if a user tries to use your Web application with an unsupported browser, the Web application should advise the user that things might not behave properly and advise them to update their browser.
      This cross-browser compatibility issue is a major frustration for most Web application developers. They see new technologies surfacing in newer browser versions—like Dynamic HTML (DHTML), Cascading Style Sheets (CSS), XML, and HTML behaviors—that would allow them to create very sophisticated Web applications. But because of corporate policy, they must continue to support every known browser. Many developers in this situation will develop parallel versions of their Web application for the different browser versions and take care of browser identification and redirection on the server (see Figure 1). This approach allows developers to take advantage of the newest technologies without affecting down-level clients. Unfortunately, this also requires the simultaneous maintenance of multiple application versions.

Figure 1: Different Versions for Different Browsers
      Figure 1: Different Versions for Different Browsers

HTTP
      A typical client/server application supports the notion of a physical user connection. The server uses this one-to-one mapping between users and connections to track user sessions and session-related state. Web applications must behave differently. In fact, this basic concept of user connections is what makes Web applications fundamentally different from typical client/server applications.
      The reason Web applications can't use the client/server model for tracking user sessions is related to the Web's underlying protocols. The ubiquitous protocol on the Web today is HTTP. To help understand how HTTP works, let's look at a typical HTTP request.
      When the user points the browser to a given Web site, the browser first establishes a TCP connection with the corresponding Web server. Once the connection is established, the browser sends an HTTP request using the existing channel. The Web server then processes the request, sends the response back to the client, and closes the TCP connection.
      This same process is used for every HTTP request made by the browser. For example, consider a Web page with 20 embedded images. To retrieve this page, the browser has to make an HTTP request for the HTML page itself, and additional HTTP requests for each of the 20 embedded images. As you can see, this protocol is a far cry from the client/server model where the connection is made once and used for the lifetime of the session. With HTTP, the user connects and disconnects on every request.
      HTTP/1.1, which isn't supported by all browsers or Web servers, offers a new mechanism called persistent connections to optimize the connection process. With persistent connections, the server maintains the connection with the client for a period of time so the client can reuse the connection for subsequent requests.

Session, State, and Security
      Since HTTP/1.0 doesn't support the concept of persistent connections, there isn't a straightforward way to keep track of user sessions. HTTP is truly a stateless protocol. In other words, the HTTP server doesn't remember anything about previous HTTP requests. If a user sends an HTTP request and then sends another one a few minutes later, the HTTP server behaves as if it's the user's first request.
      Owing to the connectionless and sessionless nature of HTTP, Web application developers are required to implement higher-level session management. The browser and the server must agree on a mechanism for identifying users on a connection-to-connection basis.
      Once you have a mechanism for identifying users across connections, you can start associating session state with a given user. In the client/server model the session state is typically stored on the server. While this is by far the easiest solution to implement, you'll see that storing session state on your Web server can severely impair scalability. So where should you store session state in your Web application? This is one of the hardest questions that every Web application designer must face.
      Another complex consideration is security. Today, everyone is concerned with security. If you visit an online shopping site and don't see the little yellow lock show up in the browser's status bar, you go elsewhere. If you've ever tinkered with network sniffers like NetMon, you know how easy it is to intercept data submitted as clear text.
      To make things even more complicated, developers must also deal with the possibility that their Web application or its clients will live behind a firewall. Firewalls block potentially malicious traffic from reaching your corporate network. They do so by rejecting attempts to establish TCP/IP connections on unsecured ports. Remote procedure call protocols such as DCOM attempt to establish connections on arbitrary TCP/IP port ranges. Unfortunately, since the firewall has no way to know whether these ports are secured, and because they generally have no knowledge of the port negotiation protocol used by DCOM, they simply prevent this traffic from passing through. But there is a protocol that generally passes unimpeded across firewalls: HTTP, which communicates over the well-established port 80. We will discuss ways in which you can invoke code on Web servers by passing appropriate messages using HTTP.
      As you can see, designing a Web application is very different from designing a typical client/server application. Considerations you must address early include tracking user sessions and deciding where to store session state.

Managing Sessions
      There are basically two standard mechanisms for managing user sessions in a Web application. Both require session-related information to be explicitly passed between the browser and the server as part of every HTTP request. Once again, since HTTP is stateless, you must provide the mechanism for reidentifying users. We like to think of this session information as being passed either by value or by reference.
      There are two common mechanisms for passing session information by value: by using hidden form elements and embedding data within URLs, and by using cookies. For the first mechanism, as the user interacts with the Web application and the server needs to save session data for the user, the server embeds the data in hidden form elements or relative URLs. This data is passed to the client as part of the HTML page retrieved from the server. The next time the user interacts with the server, either by clicking on the submit button for the form or by clicking on a link, the session data will be passed back to the server as part of the HTTP request submitted by the browser.
      This approach, although quite cumbersome, is seen in Web applications today because it works with browsers that don't support more advanced techniques like cookies. However, you must think extra hard about security when using this approach since users can easily tamper with the session data before sending it back to the server.
      You can also use cookies to pass session data by value. Each time the server needs to save some session state for a given user, it can send a cookie containing the data back to the client. As long as the browser supports cookies, it will send the cookie back to the server every time the user makes additional requests to the same domain. This works well if you can assume cookie-compatible browsers and you don't need to save large amounts of data (cookies impose various limitations; see RFC 2109 at http://www.ietf.org/rfc/rfc2109.txt for more details). If you need to associate large amounts of data with each session, passing information by reference is much better.
      Instead of passing all of the session data back and forth within each request, you could simply pass a session identifier to the client that references the user's session data stored somewhere else (such as in memory on the server or in a database). This decreases the amount of data that has to be transmitted with every request and prevents the user from hacking the data.
      The first time the user points their browser to the Web application, the browser will not send a session ID to the server (it doesn't have one yet). The server notices that this is a new user and creates a new session ID, which is transmitted back to the browser. On every subsequent request, the browser sends the session ID back to the server so the server can identify the client (see Figure 2).

Figure 2: Passing Session IDs
      Figure 2: Passing Session IDs

      The server can pass session identifiers by using a hidden form field or embedding it within every relative link (see Figure 3). Like the pass-by-value approach, this also works on all browsers, but is much less cumbersome than the complete pass-by-value technique. The only thing you would pass as part of every HTTP request is the session ID. The server uses the session ID to look up the user's session data. This approach is commonly used today by big Web applications that have to support a wide range of Web browsers. For an example, point your browser to Amazon's Web site and view the HTML source for the pages you get back. You'll notice a session identifier embedded within every URL from the Amazon domain.
      If you can assume cookie-compatible browsers, you can also use cookies to send a session identifier back to the client. This is actually how the built-in Active Server Pages (ASP) session management works. If you have session management enabled (the default), the first time you hit an ASP page in your Web application, the ASP runtime automatically sends a cookie to the client containing the session ID. Since ASP uses in-memory cookies, you won't see the cookie on disk, but you can use NetMon to verify this behavior.

Session Lifetime
      So when does a Web session end? In a client/server application, the session ends when the user closes the client application. In the Web environment, it isn't clear when a session should end. Should it end when the user browses off the page? Or when the user closes the browser? What if the user leaves the page open for days without doing any work? Should the server keep resources allocated for that lazy client?
      The standard answer for Web sessions is a timeout period. For example, if 20 minutes go by without any activity from a given client, the server terminates the client's session and reclaims its resources. In fact, ASP's built-in session management uses a default timeout period of 20 minutes, but this value is configurable. If you're using one of the manual techniques described previously, you're responsible for determining when a session should timeout (possibly by setting the cookie's expiration date).
      Transmitting session information through a pass-by-reference technique is becoming the standard in most new Web application development projects because of its obvious benefits. When you pass by reference, you must first decide where to store the session data. Where should you store session state in your Web application? There is no single correct answer to this question. When it comes to Web applications, there is no magic bullet that will solve all of your problems. There are too many variables to consider from one project to another, and we definitely believe in using the right design for a given project.
      Nevertheless, there are certain Web application principles that will help guide you toward making the right decision for your project. But you must first understand state durability and the state's scope.

Durability and Scope
      Most Web applications contain both durable and nondurable state. Durable state is persisted to disk in order to survive system failures. Most durable state is stored in some type of relational database (such as SQL Server™) for optimal storage. Durable state is obviously more robust, but that robustness comes with a performance cost. Accessing and updating durable state requires more CPU cycles along with a data- locking scheme (usually provided by the DBMS).
      Nondurable state does not survive system failures. Session state stored in memory on the Web server is an example. If the Web server goes down, the current state goes with it. Nondurable state offers much better performance at the cost of robustness.
      Most Web applications today use a combination of both durable and nondurable state. Deciding when to use one over the other can obviously have an impact on system performance and reliability. As a rule of thumb, if the data is not critical or can be regenerated at minimal cost, use nondurable state. For all system-critical data, like user passwords, use durable state.
      Web application state can also exist at four different scopes: page scope, session scope, application scope, and external scope. Page-scoped state lives in the currently loaded Web page and exists only for the lifetime of the page. As soon as the user browses to a new page or closes the browser, the page-scoped state can no longer be accessed.
      The technique for passing session data by value (using hidden form fields and URLs) described earlier is a good example of page-scoped data. The data in the hidden form fields and URLs exists only for the lifetime of the page. Once a new page is requested from the server, the browser discards the state. This is why you need to pass that state back to the server as part of the request for the next page in the Web application.
      Session-scoped data lives for the lifetime of the user's session. As long as the user's session is still active, the session-scoped data can be accessed. Once the user's session expires, the session-scoped data is removed from the system. Data that needs to be tied to a given user for the lifetime of the user's session should be stored at this scope.
      Application-scoped data lives for the lifetime of the application. Data stored at this scope is global in nature and needs to be shared between all active sessions. As long as the Web application is running, any user can access data stored at application scope. Once the application terminates, the application-scoped data is removed from the system.
      External-scoped data lives beyond the lifetime of the Web application. This type of data is usually managed by another application that is not dependent on the Web application (such as a relational DBMS). Data stored at this scope can be accessed across multiple Web applications.
      Now that we've covered the nature of Web application state, let's focus on the different techniques for managing application and session-scoped state.

In-memory State on a Web Server
      The easiest method for managing session and application-scoped state is to use the support provided by ASP. The ASP Application and Session intrinsic objects exist for this purpose. To store data at application scope within an ASP page, you simply use the following syntax:


 Application("HitCount") = Application("HitCount") + 1
This would increment the application-wide hit count. Storing information at session scope is just as easy using ASP:

 Session("StartTime") = Now
      The ASP intrinsic objects store the data in memory on the Web server on which the ASP page is executing. As noted earlier, the ASP session management scheme uses cookies behind the scenes to match a given client with its corresponding Session object in memory. As you can see, this nondurable state mechanism is very straightforward and easy to use; the ASP runtime hides all of the pass-by-reference details, allowing you to be more productive. If your Web application is small and will never need to scale, this is the best solution for you.
      If there is even a small chance that your Web application will have to scale to a Web farm scenario (where you deploy it on multiple identical Web servers), you may want to think twice about using this strategy. In the Web farm scenario, storing data at application scope in memory on the Web server doesn't work; ASP pages running on other Web servers in the farm will not see each other's updates. You could still get away with storing session state in memory on the Web server, but now you've pinned the client to a single Web server for the lifetime of its session (see Figure 4). This completely destroys any type of dynamic load-balancing strategy you might try to implement within the Web farm. Plus, you'll probably need some expensive hardware (like the Cisco Local Director router) or software to accomplish the session pinning. In other words, you've created a very complicated mess.
Figure 4: Effect on In-memory State Storage
      Figure 4: Effect on In-memory State Storage

      Another major downside to storing state in memory on the Web server has to do with fail-over. If the Web server goes down, the state is completely lost and all users in the middle of sessions lose their data. Plus, if a user is pinned to a server and that server goes down, he is stuck until it comes back online.

In-memory State on Another Server
      Another approach is to store application and session-scoped data in-memory on a distinct server designed to manage data. When your ASP page needs to store session-related data for a given user, it sends the data to the server through a DCOM method invocation. This allows you to take advantage of dynamic load-balancing on your Web farm without pinning a user to a Web server. If one Web server goes down, the next time the client connects, he will get sent to the next available Web server (see Figure 5). This solution is going to be slower than local in-memory storage on the Web server, since you have a network round-trip each time you need to access or update data.

Figure 5: The Next-Available Web Server Approach
      Figure 5: The "Next-available" Web Server Approach

      Windows® 2000 will introduce a new technology called the In-Memory Database (IMDB). (IMDB technology has been discontinued. See the IMDB update page for more information — Ed.). This technology will, in theory, allow you to cache database tables in memory on all Web servers within a Web farm for lightning-fast access. The first release of IMDB, however, only works in single-node scenarios. IMDB acts as a write-through cache manager for a specific relational database. All updates to the IMDB tables must take place through the IMDB cache.
      To make this work in a Web farm scenario, an IMDB cache must exist on each Web server. Problems arise when you need to do updates on the database tables. If one machine updates a table that lives in an IMDB cache on another machine, the IMDB machine won't see the change. You need to keep the IMDB caches synchronized on all the Web servers in the farm. This cache synchronization mechanism is planned for the next release of IMDB. Today, you can use IMDB on a dedicated state server as long as all changes go through the IMDB cache (see Figure 5). As you can see, IMDB gives you the best of both durable and nondurable state.
      Probably the most common approach today for storing application and session-scoped state in large-scale Web applications is to use a durable storage mechanism like a relational database. The database can reside on the Web server at first. Then, when the site needs to scale to a Web farm, it can be moved to a dedicated database server. Shopping cart applications that keep your purchase data around for days or even weeks are probably using this strategy. Although this strategy offers lower performance than the nondurable solutions, it allows your application to take advantage of dynamic load-balancing and fail-over strategies. Furthermore, using durable storage makes system failures much less of a problem.

Client State
      Up to this point we've been focusing on state management strategies for the server. Although for application-scoped state there really isn't any other alternative, there are several techniques for storing session-scoped state on the client. As you'll see, this client flexibility offers some very compelling advantages over server solutions.
      There are a few mechanisms available for storing nondurable session state in-memory on the client. You can use the IWebBrowser2 PutProperty and GetProperty methods for storing and retrieving any variant-compatible data type in Internet Explorer 4.0. You can even use the PutProperty method to store an automation-compatible object reference. If your object implements the IDiscardableBrowserProperty interface, the browser will automatically call release on your object if it hasn't been accessed for ten minutes.
      You can also use cookies to store nondurable session state in-memory on the client. If the server sends a cookie to the client without an expiration date, the cookie becomes an in-memory cookie and will not be saved to disk. It remains in-memory until the user closes the browser (once again, this is how the ASP session management works).
      Finally, Internet Explorer 5.0 offers a new behavior called saveHistory for storing nondurable session state in-memory on the client. This behavior allows you to save data that can be accessed across page transitions and is very easy to use from scripting languages.
      All of these techniques for storing session state on the client take a heavy burden off the server. They allow you to take advantage of the client's memory resources instead of using up valuable server resources. Plus, they all support dynamic load-balancing and will not be affected by system failures on the server (see Figure 6).

Figure 6: Client-side Session State
      Figure 6: Client-side Session State

      The biggest problem with this approach is cross-browser compatibility. None of these techniques are universally supported across browsers, and most of them—with the exception of in-memory cookies—are supported by Internet Explorer 4.0 and later.
      You can take things a step further and store session state on the client's disk. You can accomplish this today using persistent cookies (cookies with an expiration date). Internet Explorer 5.0, however, offers a much more powerful and flexible solution with its new userData behavior. The userData behavior allows you to store state as XML on the client's disk. This allows you to add rich structural meaning to your data store, and it gets you around the standard 4KB cookie limit. Using this approach, you get all the same advantages described above, plus the ability to persist state across user sessions. If the user closes their browser and comes back to your application two days later, the state will still be on their disk and available for use. The downside, once again, is cross-browser compatibility.

Do We Really Need Session State?
      All of the state management techniques described here are being used today in Web applications. Some work better than others depending on the given project's requirements. Now let's stop and think about a fundamental question: why do you need these strategies for managing session state?
      The answer lies in page transitions. Each time a user interacts with the Web server, the browser loads and renders a new HTML document. Once the new document is loaded (as the result of a page transition), the data contained in the previous HTML page is no longer accessible. In other words, page-scoped state != session-scoped state. Since page-scoped state is not equivalent to session-scoped state, you need an additional mechanism (like the ones described earlier) for preserving state across page transitions. But what if it were possible to devise a design that changed the formula to page-scoped state == session-scoped state? If this worked, you wouldn't have to worry about managing session state outside the scope of a given page. You can make this formula hold true by avoiding page transitions within your Web application. Then any piece of data stored at page scope would also exist at session scope.

A One-page Web Application
      Avoiding page transitions in your Web application gives you what we call the one-page Web application pattern. How on earth will a one-page Web application accomplish anything useful? We never said that the application couldn't interact with the Web server; it simply can't produce a standard browser page transition.
      While it's possible to produce a one-page Web application using some down-level browsers, it's never been easier than with Internet Explorer 5.0; it's as if Internet Explorer 5.0 was designed specifically for this type of Web application. The Internet Explorer 5.0 enhanced XML support combined with DHTML is what makes it possible.
      Internet Explorer 5.0 introduces the concept of an XML data island. Using an XML data island, you can contain an XML segment within your HTML document. Internet Explorer 5.0 will automatically parse the XML data and allow you to programmatically access the XML document object model (DOM) from script. The following is an example of an XML data island:


 <HTML>
 <BODY>
 This is an HTML document
 <XML ID=sessionData>
 <ORDERS>
  <ITEM ID=1>
    <NAME>Essential WinInet</NAME>
    <PRICE>34.95</PRICE>
  </ITEM>
 </ORDERS>
 </XML>
 </BODY>
 </HTML>
      XML data islands also have an src attribute that you can use to point to XML data on the Web server:
<HTML>
 <BODY>
 This is an HTML document
 <XML ID=xmlData1 SRC="orders.xml"></XML>
 <XML ID=xmlData2 SRC="getorders.asp?userid=2"></XML>
 </BODY>
 </HTML>
Notice that XML data islands can point to static XML files or even ASP files that generate XML output. Now for the interesting stuff: you can also dynamically change the src attributes of XML data islands from script.
<HTML>
 <BODY>
 <SCRIPT LANGUAGE=javascript>
 function getXML(id) {
   xmlData1.src = "getorders.asp?userid=" + id;
 }
 </SCRIPT>
 This is an HTML document
 <XML ID=xmlData1"></XML>
 <INPUT TYPE=button VALUE=User1 ONCLICK="return getXML(1)">
 <INPUT TYPE=button VALUE=User2 ONCLICK="return getXML(2)">
 </BODY>
 </HTML>
As you can see, XML data islands are a very convenient mechanism for requesting additional data from the server without forcing a page transition.
      Another new object in Internet Explorer 5.0, XMLHttpRequest, gives you direct access to the underlying HTTP protocol along with XML parsing support. For example, consider the following script (disclaimer: this only works with the final release of Internet Explorer 5.0):
<SCRIPT LANGUAGE=javascript>
 function makeHTTPRequest() {
   var xmlrequest = new ActiveXObject("microsoft.xmlhttp");
   strRequest = "<?xml version='1.0'?><user>1</user>";
   xmlrequest.open("PUT", "http://www.skonnard.com/getuser.asp", false);
   xmlrequest.setRequestHeader("Content-type", "text/xml");
   xmlrequest.send(strRequest);
   xmlResponse = xmlrequest.responseXML;
 }
 </SCRIPT>
This block of script, which can be executed in response to some user interaction, sends an HTTP request to the server and waits to receive the HTTP response. Once the script receives the response, it can use DHTML to update any scriptable element within the HTML page (see Figure 7).
Figure 7: Updating Session State
      Figure 7: Updating Session State

      Internet Explorer 5.0 also has excellent support for the Extensible Stylesheet Language (XSL). This allows you to store most of your page-scoped data as XML. You can then use XSL to transform the XML data into HTML—use XML to represent page-scoped data and XSL to describe how that data should look to the user.
      When you need to persist your XML data across user sessions, once again you can save it to disk using the Internet Explorer 5.0 intrinsic userData behavior.
      As you can see, Internet Explorer 5.0 and its extensive support for XML and out-of-band requests makes the one-page Web application very feasible. Imagine being able to save all session-scoped state within a single Web page, having programmatic access to the data via a standard DOM, and being able to easily persist the session data to disk using XML. This gives you better performance, scalability, and simplicity than any of the other strategies we've discussed here.

Techniques for Avoiding Page Transitions
      To make the one-page Web application pattern work, you must avoid page transitions in your Web application. To do so, you must eschew using the standard behavior for form actions and anchor elements. In other words, you'll have to implement custom actions in script and override the element's default behavior. Your script can use DHTML along with XML data islands or the XMLHttpRequest object to make HTTP requests that don't initiate page transitions:

<SCRIPT LANGUAGE=javascript>
 function submitRequest() {
   xmlData1.src = "getorders.asp?userid=1";
 }
 <SCRIPT>
 <FORM ACTION="javascript:submitRequest()">
 <INPUT TYPE=submit VALUE=Submit>
 </FORM>
      To make the user feel like there was a page transition (and that something actually happened in response to the user's interaction), you can use DHTML to hide, show, or update elements on the page.
      We should also point out that you could accomplish this type of functionality on down-level browsers using more traditional technologies like Java applets or ActiveX® controls. The downside is that they're more difficult to implement and require downloading binary images. Plus, if you want to take advantage of XML on these down-level systems, you'll have to implement most of the XML functionality yourself. (See Ken Spencer's Beyond the Browser column in this issue for a discussion of how to do this in Visual Basic.)

A New Role for ASP
      When you start thinking about the one-page Web application pattern, ASP pages should seem more like remote functions for generating XML data. ASP pages are definitely not limited to returning HTML—they can return any type of data that makes sense to your Web application client, such as XML. Web application developers using this pattern will benefit from creating reusable ASP-compatible COM objects to handle XML generation.
      More and more database vendors are adding native XML support to their systems. Even today, you can use ADO 2.1 to persist a recordset object to XML. As more tool vendors add XML support to their portfolios, Web applications designed around XML will continue to benefit.

Conclusion
      Developing powerful and scalable Web applications is not easy; there is much more to Web application design than deciding how to lay out your HTML elements. Hopefully, you now understand the fundamental differences between designing a typical client/server application and a highly scalable Web application. You should also appreciate how session and state management influence the overall performance and scalability of your system.
      One thing we'd like to emphasize is that there is not a single design that works for all projects. But given a particular project's requirements, there is probably a design strategy that will give you optimal performance and scalability. The concepts presented here should help you feel more prepared to discover the best design for your project.
      We introduced a new design pattern called the one-page Web application, which offers a simplified state management strategy that doesn't compromise scalability or performance. This design allows your Web servers to support more concurrent users because it's capable of leveraging the client's system resources. Internet Explorer 5.0 makes it possible to implement such a radical new design. Due to space constraints, we've barely touched upon the many implementation details that you're probably craving right about now. Not to worry; in a future article we'll present a comprehensive sample application that brings the one-page Web application to life.

MSDN
http://msdn.microsoft.com/library/techart/msdn_csarctop.htm


From the September 1999 issue of Microsoft Internet Developer.