We'll see how the current DNA architecture succeeds in these areas in the next chapter.
Windows DNA had five major design objectives. These are common themes that run through the architecture, guiding the design decisions at each step. Without these, the architecture would be incoherent, and would not address the challenges of network applications. These design objectives are:
- Autonomy
- Reliability
- Availability
- Scalability
- Interoperability
We'll discuss each of these in turn.
Autonomy
Autonomy is the extension of encapsulation to include control of critical resources. When a program uses encapsulation, as with object oriented programming, each object protects the integrity of its data against intentional or accidental corruption by some other module. This is just common sense extended to programming: if something is important to you, you will take care of it. No one else will protect it as well as you will.
Unfortunately, client-server computing violates encapsulation when it comes to resources. A server, no matter how well written and robust, can only support a finite number of connections. System memory, threads, and other operating system objects limit it. Database programmers see this expressed in the number of concurrent connections an RDBMS (Relational Database Management System) can support.
Conservation of system resources was a sensitive topic in departmental level client-server applications. It becomes an essential issue as we build mission-critical systems at enterprise scale. The sheer multiplicty of components and applications in a distributed system puts pressure on any one server's resources. The dynamic nature of resource usage makes tracking and managing resources hard.
Extending encapsulation to resources should suggest that the server, which is critically interested in conserving its resources, is best positioned to manage those resources. It is also best suited to track their utilization. The server is the only entity that has a global view of the demands on it. Consequently, it will try to balance those demands. It must also manage secure access to its resources - access is a resource no less important than a physical entity like memory or database connections.
The addition of the application logic tier between presentation tier clients and data servers clouds the issue of resource management. The data server knows the demands presented to it by the components and servers on the application logic tier. It cannot know, however, what kind of load is coming into that tier from the clients on the presentation tier (or from other servers within the application logic tier, for that matter). Thus, application logic tier components must practice autonomy as well.
Just as a component is a client of data servers, it is also a server to presentation clients. It must pool and reuse critical resources it has obtained from other servers, as well as managing the resources that originate within it. A component managing access to a database, for example, will likely acquire a pool of connections based on its expected demand, then share them across clients that request its services.
Servers in any tier, then, practice autonomy in some form. They practice native autonomy on resources they originate, and they act as a sort of proxy for servers they encapsulate. In the latter case, they must not only share resources, but also ensure that access control is respected. In the case of a database, for example, the component acting as a proxy knows the identity (and by implication the access permissions) of the requesting client. Because the component acts as a proxy, using shared resources, the data server no longer has direct access to this information. Instead, the database typically establishes access based on roles. The proxy component is responsible for mapping a particular client's identity to a role within the system as it grants access to the resources it serves from its pool.
Reliability
Computers are reliable, aren't they? Surely if we submit the same set of inputs to the same software we'll obtain the same results every time. This is certainly true from the vantage point of application programmers (hardware engineers might have a few quibbles). As soon as we open an application to the network, however, the challenge of maintaining the integrity of the overall system - reliability - requires our involvement.
If you have ever programmed a database application, you have encountered the classic bank account example. When an application transfers funds from one account to another, it must debit the losing account and credit the gaining account. A system failure between the two operations must not corrupt the integrity of the bank.
Client-server applications only had to concern themselves with the failure of the server or loss of a single connection to ensure the integrity of the application. A three-tier distributed system introduces many more points of failure. The challenges we enumerated earlier for network applications, especially connectivity, resource collection, and availability, offer the possibility of failures that are harder to unravel.
Relational databases offer transactions within their bounds; a network application using multiple data stores requires a distributed transactional capability. If the system experiences a loss of connectivity, the transaction service must detect this and rollback the transaction. Distributed transactions are difficult to implement, but are critically important to the success of a network application. Their importance is such that distributed transactions must be viewed as critical resources requiring the protection and management we discussed under the goal of autonomy.
Availability
This goal is concerned with the ability of the network application to perform its functions. Such an application contains sufficiently many resources prone to failure that some failure must be expected during the course of operation. Optimal availability, then, requires that the network application take the possibility of failure into account and provide redundancy, either in terms of extra hardware or duplicate software resources, or in the provision for gracefully dealing with failure.
A network is inherently redundant in that it has multiple computers on the network. To achieve high availablity, a network application must be designed with the known points of failure in mind, and must provide redundancy at each point. Sometimes this is a matter of hardware, such as RAID disk drives and failover clusters, and other times it's a matter of software, as in web server farms. Software detects the loss of one resource and redirects a request to an identical software resource.
In the monolithic world, if we had our computer, we had our application. Network applications, however, give the illusion of availability to the user whenever their machine is available. The actual state of the network application's resources, however, may be very different. It's the goal of availability to ensure that the resources of the network are deployed in such a way that adequate resources are always available, and no single failure causes the failure - or loss of availability - of the entire application.
Availability is the goal behind such buzzwords as "five nines", that is 99.999%. If you are going to the expense of fielding a network and writing a distributed application, you expect the application to be available. A monolithic application running on commodity PC hardware is scarcely capable of hosting mission-critical functions. Windows DNA aspires to host such functions on networks of commodity computers. Availability is a make or break point for Windows DNA.
Scalability
It would be close to pointless to deploy a network for a single-user application. One of the points of network application architecture is to efficiently share resources across a network on behalf of all users. Consequently, we should expect our network applications to handle large volumes of requests. Each of, say, 100 users should have a reasonable experience with the server, not 1/100th of the experience and performance provided to a single user of the application.
Scalability measures the ability of a network application to accommodate increasing loads. Ideally, throughput - the amount of work that can be completed in a given period of time - scales linearly with the addition of available resources. That is, if I increase system resources five times (by adding processors or disks or what have you), I should expect to increase throughput five times.
In practice, the overhead of the network prevents us from realizing this ideal, but the scalability of the application should be as close to linear as possible. If the performance of an application drops off suddenly above a certain level of load, the application has a scalability problem. If I, say, double resources but get only a 10% increase in throughput, I have a bottleneck somewhere in the application.
The challenges of network applications and the responses we make to them - distributed transactions, for example - work against scalability. Architects of network applications must continually balance the overhead of distributed systems against scalability. A scalable architecture provides options for growing the scalability of an application without tearing it down and redesigning it. An n-tier architecture like DNA helps. If you encounter a bottleneck on any single machine, such that adding additional resources to that machine does not alleviate the bottleneck, you are able to off-load processing to other machines or move processing between tiers until the bottleneck is broken.
Interoperability
The challenge of platform integration arises from the fact that organizations will end up possessing dissimilar hardware and software platforms over time. In the past, organizations sought to fight this through standardizing on one platform or another. The sad reality of practical computing, however, is that standardization is nearly impossible to maintain over time.
Sometimes there are sound technical reasons for introducing heterogeneous platforms - a given platform may not be sufficiently available or scalable for a particular need, for example. Other times, the problem arises from a desire to protect the investment in outdated hardware. Sometimes, it's simply a matter of the human tendency to independence and diversity. Whatever the cause, interoperability - the goal of being able to access resources across dissimilar platforms and cooperate on a solution - is the answer. Any architecture that claims to be suitable for network applications must address the problems of differing system services and data formats that we described under the challenge of platform integration.
Summary
We've used this chapter to set the scene for why we need to consider a specific architecture like Windows DNA.
We saw what requirements we're likely to have of our applications and of our platform - they should present a unified view of data from multiple data sources, allow a user to update data, possibly provide full e-commerce capabilities, be fast, and be scalable to thousands of concurrent users. We also saw some of the problems inherent in network applications (communications, concurrency, state, latency, and encapsulation). We then went on a sideline track to see how network applications have evolved from monoliths to distributed component-based n-tier architectures. Finally, we looked at what DNA professes to achieve for your applications (autonomy, reliability, availability, scalability, and interoperability).
Although we've not seen exactly what DNA is, we have seen what it's supposed to do for us. In the next chapter, we'll take a look at what's in DNA, and see how these things can solve the problems we've looked at here.