Components and Web Application Architecture
By Alex Homer, et al.
Chapter 13 from Professional Active Server Pages 3.0, published by Wrox Press
Up to this point in the book, we have looked at how to use Active Server Pages to dynamically build Web pages. ASP scripting allows us to take information passed in by the browser and dynamically create an HTML (or DHTML) page to send back to the browser. When building this page, we can pull in information retrieved from databases such as SQL Server or the Microsoft Data Engine (MSDE), and add this to the HTML page to be sent back to the browser.
But ASP is more than just creating dynamic Web pages. We have the ability to tie together sets of pages into Web sites that subsequently begin to function like applications. And, thanks to the Application and Session objects, our applications can be stateful, allowing us to share information between an individual's requests to a site, and even between all the users of a site. These applications have all the power and functionality of traditional applications, along with some tremendous benefits.
As we make this transition to building larger and more scalable applications with ASP, we will want to take advantage of the technologies that allow us to do so with greater ease. If an application is truly scalable, it has the ability to handle a very large number of users with the same facility, and only slightly poorer performance, than with which it handles a very small number of users. As builders of Web sites that could be generating an income for us, the ability to support more users means more dollars in our pockets. One key way of making applications more scalable is by building them out of components. In this chapter and those that follow, we will take a look how to design applications out of components.
Specifically, we will look at:
The architecture of distributed applications, including Windows DNA
What makes up a Web application
What are components
A quick introduction to ActiveX and COM
The three types of components and why we should use them
Building Web applications out of components
Designing a component-based Web application
To get us started, let's look at the bigger picture of application architectures.
On This Page
Distributed Application Architecture
Component Application Design
An Application Design Case Study
Distributed Application Architecture
One of the key elements of any application design is the system architecture. The system architecture defines how the pieces of the application interact with each other, and what functionality each piece is responsible for performing. Whether we like it or not, in the Web-based world we inhabit, we are more often than not going to be building applications that exist in a distributed environment. In fact, the ASP applications that we have been looking at up to this point in the book are actually distributed applications.
A distributed application utilizes the resources of multiple machines or at least multiple process spaces, by separating the application functionality into more manageable groups of tasks that can be deployed in a wide variety of configurations. There are a number of benefits to dividing applications up into pieces, not the least of which are reusability, scalability, and manageability.
Ultimately, dividing up an application in this manner results in the creation of a series of application layers or tiers, each of which is responsible for an individual, or atomic, element of the application's processing.
Tiered applications can be characterized by the number of layers that information will pass through on its journey from the data tier (where it is stored in a database typically) to the presentation tier (where it is displayed to the client). Each layer generally runs on a different system, or in a different process space on the same system, than the other layers.
Two-Tier Applications (Client/Server)
Let's look briefly at the 2-tier client/server architecture. Typically, we have a user's PC for the client (front-end) and a network server that contains the database (back-end). Logic is divided between these two physical locations. Usually the client contains most of the business logic, although with the advent of stored procedures, SQL language routines allow business logic to be stored and executed on the database server:
The 2-tier scenario works very well when you have a small business that only uses, or needs, a single data source. However, the goal of most businesses is to grow. As the business grows, so will the database and its requirements. Unfortunately, the 2-tier approach does not scale very well. If your business rules change then the application needs to be rebuilt and redeployed. In addition, there are factors such as the maximum number of simultaneous database connections that prevent this architecture from ever being of much value in a distributed setting with more than a few users.
Three-Tier and N-Tier Applications
Due to the limitations of the 2-tier client-server architecture, distributed applications are often divided up into three or more tiers. Components in each of these perform a specific type of processing – there's a User Services (Presentation) tier, a Business Services tier, and a Data Services tier in a 3-tier application.
The main distinction between this 3-tier architecture and your traditional 2-tier client-server architecture is that, with a 3-tier architecture, the business logic is separated from the user interface and the data source.
Breaking up applications into these separate tiers or sections can reduce the complexity of the overall application, and results in applications that can meet the growing needs of today's businesses. n-tier applications are just 3-tier applications that might further sub-divide the standard User Services, Business Services, or Data Services tiers. In any case, an application with more than two tiers can be considered an n-tier application.
In this type of application, the client should never access the data storage system directly. If it did, it would be circumventing the business rules of the application and would thus be unable to ensure that the data on display to the client was correct.
The separation of the various aspects of an application into n tiers allows for any part of that application to be modified without having to change the other parts, allowing developers to specialize in designing and developing a specific tier or tiers. Similarly, developers can also take advantage of the development tools that specialize in the development of that tier, rather than making use of general purpose tools, which are sufficient to build an entire application but are lacking in terms of powerful features.
Before we look at the three basic types of services in more detail, there is one more variation on the n-tier theme we need to look at.
Windows DNA refers to Microsoft's Windows Distributed interNet Architecture, and not DeoxyriboNucleic Acid. It is Microsoft's latest and greatest development model for the Windows platform, the goals of which are to:
Provide the architecture to develop and deliver robust, scalable, networked applications on the Windows server platform
Make both new and existing applications and data sources available to a wide range of clients, from traditional desktop applications, to Web browsers, to Internet appliances
Windows DNA is about Infrastructure
Windows DNA provides a roadmap that helps make developing distributed applications easier, because it's all about encouraging the use of already available application and operating system services to minimize the amount of infrastructure code required to create a distributed application.
Just think about all the times that you wrote hundreds of lines of code to provide transaction processing, message queuing, security, or custom database access in your applications. Now imagine all of these distributed application requirements being lifted off your shoulders. Instead, Microsoft will take care of these details for you, by providing you with a set of efficient and programmable services, leaving you to concentrate on the business requirements of the application.
The bottom line is that developing distributed applications with the Windows DNA model will result in higher performance, more reliable, and scalable distributed applications, delivered faster than ever before, at a lower cost.
Before Windows DNA, usually only large-scale or enterprise applications distributed application processes. However, because of the application infrastructure, Microsoft continues to build and enhance its applications, making it easier to develop distributed applications. Soon many of your smaller network applications will be using the Windows DNA model instead of the traditional two-tier client-server model.
Windows DNA is about Interoperability
Windows DNA can interoperate with, or talk to and make use of, existing legacy systems so you can leverage and extend current technology investments. Try telling the Chief Information Officer of a medium-size manufacturing company that you want to scrap all of their existing software and hardware so you can develop new distributed applications based on the Windows DNA model.
It won't happen overnight – just like converting all companies that use Oracle database systems to Microsoft SQL Server 7.0 database systems. Microsoft has finally realized that not everyone will or can afford to convert every legacy system to run on the newest Microsoft platform. The legacy systems will remain in place, but will need to be integrated into our new applications. The evidence of this discovery can be found in the Windows DNA model, with its interoperability and support for industry standards. For example, the Windows DNA model supports the open Internet standards for HTML and XML.
The missing link between systems is DCOM, which allows applications to communicate across processes and across machine boundaries to compiled components that provide services, all the while without having to know where the service is located or how it is implemented. It allows developers to create service-based applications while using component-based development methodologies.
While DCOM is mainly a Windows platform service (I say mainly because DCOM has been ported over to some versions of Unix) the open Internet standards supported by the Windows DNA model ensure that you can create cross platform distributed applications.
Windows DNA also makes provisions for the access of legacy data with its Universal Data Access (UDA) initiative, by allowing you to use the same data access library to run queries against your SQL Server and your legacy AS400 databases, for example. This is achieved by several sets of technologies all knowing how to interact with that same library, and then having the ability to access both relational and non-relational data from multiple different sources on their own system.
Windows DNA is about the Internet
When Microsoft introduced the Windows DNA architecture in September 1997, they introduced a framework for creating new Internet applications as well as integrating the Web with already existing client-server and desktop systems. The Internet is a great application platform for two main reasons:
The Internet allows developers to create applications that are platform independent without making developers learn a new proprietary language.
The Internet provides a flexible mechanism for deploying applications. Deployment is a major problem for lots of organizations, especially when you're talking about deploying a large-scale, enterprise application to several separated locations.
Windows DNA Services
As we've seen, Windows DNA provides a range of services that provide the necessary plumbing and infrastructure upon which we can build our application in relative ignorance.
The presentation services of Windows DNA deal with how the application interacts with the user. This user can be located on the same computer, on a different computer on the same LAN, or on a computer on the other side of the world connected by the Internet. A key concept behind Windows DNA is that the accessibility of the application is not affected by the type of client that the user has. In order to provide this flexibility, Windows DNA supports a wide range of client types and a wide range of tools with which to build these presentation services.
Even though a Windows DNA application can support many different client types, we can characterize them into four distinct categories, two of which are traditional "fat-client" while the other two are "browser-based". The traditional applications are either written to take advantage of a network connection, if one is available, or written such that they simply will not function if a network connection is not present. The browser-based applications meanwhile can either be browser-neutral or tailored for a specific browser.
An Internet-Enhanced client is one that takes advantage of the features of DNA and can also make use of, but is not reliant upon, Internet connectivity. Take, for example, Microsoft's Office 2000 and Visual Studio. These applications support unified browsing by embedding hyperlinks from within the application, host the browser for the display of documentation written in DHTML, and provide the capability to download updates over the Internet seamlessly.
An Internet-Reliant application is one that is written as a traditional Windows application, but which requires a network connection to a server in order to function. This type of application is generally used when the user interface of the application goes beyond what is efficiently developed using a Web-based client. It could also be an enhancement to an existing Windows application to convert it into a Windows DNA application. The primary characteristic of this type of application is that it requires a connection to the network in order to function, as opposed to an Internet Enhanced application, which will run on a stand-alone machine as well as on a networked one.
"Fat-client" applications can be created using any of the Windows development tools, such as Visual Basic, Visual C++, Delphi, or any other tools that will create an executable application. The reliance on a network connection can be added in several different ways. The application could link to the Windows Sockets library in order to connect to a server via TCP/IP over the local network or Internet, for example. Alternatively, it could host an embedded version of Internet Explorer to provide access to information on the network. In any case, the application interacts with (and depends on) a server somewhere on the network in order to function and won't work without a permanent link to it.
Whereas the two previous application types that we looked at are part of an executable application, the second two are browser-based applications, so called because they use HTML as their presentation mechanism. This HTML is retrieved in real time from a Web server using HTTP as a data transfer protocol. The HTML provides a user interface to the client that allows the user to interact with the application using a Web browser.
Even though HTML is a specification, different browsers often support extra functionality not found in the ratified standard. When a user interface is developed that requires the use of extensions to the HTML specification, this is known as a Browser-Enhanced application. For a Windows DNA application, this usually means that the user interface is targeted for delivery in Internet Explorer. With a specific browser as a delivery target, the developer can utilize technologies such as ActiveX controls, Dynamic HTML, or IE Behaviors to make the user interface more like a traditional Windows application. Even in this case, the user interface is delivered to a Web browser using the HTTP protocol from a Web server. Applications such as this are generally found on corporate intranets, where the IT department can control the target browser platform. If the application developer can expect a certain version of a browser, then they can create a Browser-Enhanced application and know that it can be executed successfully.
One of the drawbacks of browser-enhanced user interfaces is that they generally require a specific browser in order for the user to work with the application. Whenever a new technology, such as a new browser version, is made available, there is usually a time lag, sometimes up to several years in length, before a large installed base of that new browser version becomes available. During that time, if the developer targets these new features, then they risk making their application unavailable to a large portion of their audience.
To avoid this problem, the developer can choose to target an earlier browser version, which will make their application accessible to wider population. These types of clients are known as Browser-Reliant. This may sound a bit deceiving, but the name refers to the fact that the user interface is reliant on a browser and not a specific version of that browser. It is probably not effective for a developer to develop an application that works with ALL browsers. Generally, the current standard for applications requiring a wide deployment will be a browser target of the version 3.x browsers from Netscape and Microsoft.
A Windows DNA application relies on the available application services to store and execute the core of its application logic. This business logic tier is where the application-specific processing and business rules are maintained. The Windows DNA services for developing these application components include Web services, messaging services, and component services.
Component-based applications have been the standard method for creating applications for the better part of the last decade. From their earliest days as DDE, then OLE, OLE2, and finally today's ActiveX, component applications for the Windows platform have become widespread. It's not surprising really considering how much easier development is using components – the whole process now has a new model which consists of wiring pre-built components together to create an application. Why is this possible? Because every component has a number of publicly accessible functions that other components can discover and use. The end effect is that components are simple to reuse and allow different applications to work with each other.
To help component-based applications grow into enterprise-level applications, Microsoft added the Microsoft Transaction Server to provide services for the development, deployment, and management of component-based distributed applications. By encapsulating this plumbing into a system-level service, it freed developers from having to explicitly add this support to their applications.
With the release of Windows 2000, we saw the introduction of COM+. COM+ unifies the programming models of COM and MTS and makes it even easier to develop distributed applications by eliminating the need to handle both services individually. This allows the developer to build applications faster, easier, and ultimately cheaper by reducing the amount of code required to make use of these component services.
Since not all parts of a Windows DNA application need to be located on the same computer, or even in the same physical location, it is critical that developers be able to communicate with these disparate systems. And, in the networked applications paradigm, there also needs to be a way for disconnected clients to communicate with the application. Messaging is a technology that permits this communication. Microsoft Message Queue Server (MSMQ) makes it easy to integrate applications by implementing a message delivery mechanism between applications. This allows the developer to build reliable applications that work for both connected and disconnected clients. MSMQ also offers seamless interoperability with other message queuing products, such as IBM's MQSeries.
The application services of a Windows DNA application need a way to interact with the presentation layer on the client. In this case, that way is the Hypertext Transport Protocol, a mechanism which Microsoft's Web Server, IIS, already provides as a service for anything that cares to use it. In fact, IIS not only supports the delivery of static Web pages to a client, it can also be used to dynamically create client presentations through its integration with Active Server Pages. But you knew that already, didn't you?
As we noted earlier, Universal Data Access is Microsoft's strategy for providing access to information across the enterprise. It provides high-performance access to a variety of information sources, including relational and non-relational data, and an easy-to-use programming interface that is both tool and language independent. UDA does not require the expensive and time-consuming movement of data into a single data store, nor does it require commitment to a single vendor's products. It is based on open industry specifications with broad industry support, and works with all major established database platforms. The two primary components that provide data services are ActiveX Data Objects (ADO) and OLE DB, both of which we've already covered in this book.
The Structure of the Web
The structure of the Web is a lattice of HTML-formatted pages, distributed from computers known as Web servers to client computers, and viewed using tools called Web browsers. At the most basic level, there is no difference between a server that has thousands of pages and a server that just has one or two. It is by linking these pages together in some form that a set of pages becomes a Web site, and by adding some additional logic, a Web site becomes a Web application.
The basic unit of a Web interaction is the Web page itself. A Web page is a text file that is marked up using HTML. It is sent to a browser from a Web server based on a request from that browser. The browser parses the information in the HTML file, and the resulting user interface is displayed within the browser itself. The role of the Web server is to listen for a request from the client, parse the request to determine the page that the client requested, retrieve that file from the server's storage area, then transmit that file to the client. At this point, the server forgets everything about sending that file to the client, except for maybe placing an entry into a log file. It is this "connection-less" nature that gives a Web server its scalability, but in turn makes it a challenge to create meaningful applications without some additional support.
A Web site consists of a set of related Web pages grouped together by some means. Generally, a Web site is all of the pages that exist on a server, or within a folder on that server. For example, all of the pages that are on the http://www.wrox.com server are considered part of that Web site. The correlation between the pages on a site is maintained by the links within each page on the site. The links on each page in the site will take users to other pages within the site. In this way, the pages that make up the site internally maintain the hierarchy of the site. These pages are still subject to the restriction of the Web server architecture, in that the server does not maintain information about the series of requests from a particular client. So while this related set of Web pages that make up a Web site are beginning to look more like an application, there are still some missing components.
Windows DNA applications are applications in the traditional sense, in that they provide a service to the user of the application. They are different in the way that they are created as well as in the components that make them up. A traditional application requires a special set of files during development, but distributes different outputs. For example, a Visual Basic application has a .vbp project file, multiple .frm, .cls, and .bas files, as well as a set of OCX components that make up the application project. Prior to the application being distributed, these files are compiled into a set of executable files for distribution and execution. The resulting executable does not require the presence of the source code files that were used to develop it.
Script-based Web applications, on the other hand, are composed of the same set of files used during development and after deployment. There is no compiled executable file produced that becomes the Web application. For example, the .htm, .asp, and .dll files in your Web project are the same files you deliver to your production Web server. The source code, or script, in these Web files is executed on the client or server only when a browser requests the Web page. Creating these applications builds upon the architecture of the World Wide Web, but there is some added complexity and functionality in order to have these files function as an application.
Web Application Design
In building a Web application, there are a number of new aspects of application design and development that the developer must take into consideration. While the flexibility of Windows DNA allows for a wide variety of clients accessing a wide variety of services, we will be focusing on a Web application. In a Web application, we will look at using a browser as the primary user interface. The information in our Web application will flow from server to client using the HTTP protocol. Our Application server will be Microsoft's Internet Information Server functioning as both a Web and Application server. Finally, our business and data access logic will be encapsulated within COM+ components and serve as the plumbing that links our application together.
The Browser as the User Interface
For a Web application, the user interface is presented within a Web browser. This means that the client presentation can either be Browser-Enhanced or Browser-Reliant. The type that you choose for your application should be based on the installed browser base of your target audience. If you can guarantee that most of your users will be using a particular browser level, then you should consider leveraging the features of that browser level. If there are a wide variety of browsers in use, then you may only be able to support a Browser-Reliant client presentation type. One of the advantages of using Active Server Pages to deliver the application is that it has the capability to determine with what browser the current user is accessing the application. This was covered in Chapter 6 with the Browser Capabilities component. By knowing the browser type of the current user, you can dynamically change your client presentation to support the enhanced characteristics of that browser.
HTTP as the Transport
The communication layer between the client and the server is critical to the design and implementation of the application. The HyperText Transport Protocol (HTTP) defines how the request made by the client is received and handled by the server, and then how the information is sent back to the client. Depending on the type of client presentation being supported, there can be different types of information that flow using this protocol. For the basic support of a Browser-Reliant client, the information that flows over HTTP is limited to the HTML that makes up the page, graphical images to enrich the interface presentation, information-bearing cookies, and possibly some client-side scripting to provide interactivity on the client. With an Enhanced client, special features such as Remote Data Services or COM over HTTP may also be communicated over the transport protocol. But support of these is reliant on the capabilities of the browser at the client. In either case, the HTTP protocol does not understand the concept of a persistent connection between client and server.
IIS and ASP as the Application Server
The combination of Microsoft's Internet Information Server as the Web server and Active Server Pages as the Application server provides the application component of our Web application. For Web pages and Web sites that serve up static Web pages for display, IIS can function by itself to provide the information. But when a Web application demands a dynamic display of information, we need to link the Web serving capabilities of IIS with the dynamic page generation and object integration capabilities of ASP to deliver a more robust and dynamic Web application. The scripting capabilities of ASP allow us to support business logic inside of our scripts, link with business logic components, directly access databases via ADO, and make use of data components to retrieve information.
COM+ as the Plumbing
The component-based design of Windows DNA applications is made possible by their reliance on COM+ for component services. While a fully functional application can be constructed out of ASP script and HTML, it is when the business logic and data access are separated out into components that a more robust and scalable application begins to emerge. By placing the business and data access logic inside of components that have been compiled prior to execution, the performance of the application increases. And, with the increase in performance, we have laid the path for a corresponding increase in the number of users that the application can effectively support.
With all of this talk about components and component-based applications, it is important to understand just what a component is. We have seen a number of them used in ASP already in this book – remember that ADO is a set of components that support access to data sources. There are six components intrinsic to ASP 3.0 that allow a developer to access certain parts of the Web application. The concept of components is critical to the creation of scalable Web applications.
What is a Component
A component is an object that contains code to manipulate data from one form to another, and which provides access to that code through a well-specified set of publicly available services.
This is a very literal definition of a component.
In a more practical sense, a component is an encapsulated piece of code that performs some function for an application.
This function could be the processing of a business rule, like the computation of a sales tax, or it could be the retrieval of some information from a database for an application. The key characteristic of a component is that when it is created for use, the code for the component, as well as the information associated with the component, are packaged together. In this way, if there are multiple versions of the same component in use at one time, each one keeps its information separate from the others. There is no danger of information in one polluting the information of another.
In addition to the type of work that it performs, a component is also defined by its interface. To understand about interfaces in more detail we need to take a closer look at COM.
COM and COM+
Throughout this chapter, we have talked about components and COM rather freely. COM stands for the Component Object Model.
COM is an outgrowth of the object-oriented paradigm and is a specification that is based on a binary standard for reuse through interfaces.
COM is an object model developed by Microsoft and implemented on all of the Windows platforms. It forms the basis for all applications developed for Windows in that nearly all interactions with the base operating system are through COM-defined interfaces. COM defines a standard for component interoperability – that is, the ability of components to interact with one another. It does not specify the language that the components are written in – it merely specifies how components need to communicate with one another and with the operating system itself. By specifying the interoperation standards that components must adhere to, it makes it easy for components created by different developers, even developers from competing companies, to work together in an application. This means that components written for COM can be reused without any dependencies from the language it was written in. It doesn't matter if an application contains components written in Visual Basic, C++, Java, or even COBOL, just as long as these components follow the COM specification.
COM+ is a set of services that combine COM with Microsoft Transaction Server (MTS) on Windows 2000 systems. MTS provides the facility to manage the lifetimes of components. Rather than having to worry about the creation and destruction of objects for their application, COM+ has the ability to manage that for us, so we can focus on implementing their business logic. With the introduction of COM+, the functionality of MTS has been merged into the operating system and is now called Microsoft Component Services. We'll take a cursory look around this in the next section, but for a more detailed discussion, you should take a look at Chapter 15.
With the release of COM+, Microsoft Transaction Server has ceased to exist as a separate entity on Windows 2000. Its functionality is now a basic part of the operating system under the moniker "Microsoft Component Services", which should indicate that there's more under this roof than just the management of transactional services and components' lifetimes.
Along with a new name, the Component Services also offer some new functionality in the way that components are handled over MTS. As the operating system service – and not the developer – handles more and more of these features, our lives should become much simpler in the pursuit of developing robust and scalable components. Here's what's on offer.
Component Load Balancing (CLB) – CLB allows multiple application servers to provide the same COM+ object for use in an application. When that object is needed, the creation request is sent first to the CLB Server which then redirects the request to an appropriate application server, based on certain criteria (like how busy and how far away from the machine running the application it is). The client application then interacts with this server for the lifetime of the component. Load balancing would be implemented at the COM class level but at the time of writing it was unclear as to whether CLB would be included in the initial release of Windows 2000. It may make an appearance as part of a Service Pack, or in a future release of Windows 200x. (I guess this will become the new OS naming convention.)
Queued Components – Combines the features of COM and MSMQ to provide a way to invoke and execute components asynchronously. Processing can occur without regard to the availability or accessibility of either the sender or receiver. When a client calls a queued component, the call is made to the Queued Components recorder, which packages it as part of a message to the server and puts it in a queue. The Queued Components listener retrieves the message from the queue and passes it to the Queued Components player. The player invokes the server component and makes the same method call.
In-Memory Database Support – The In-Memory Database (IMDB) is a transient, transactional database-style cache that resides in RAM memory and provides extremely fast access to data on the machine on which it resides. IMDB can be used by COM+ applications that need high-speed database lookup capabilities or applications that need to manage transient state. Data inside an IMDB can be accessed using ADO or OLE DB interfaces from a COM+ object. As with load-balanced components, the IMDB technology has not found its way into the first release of Windows 2000. It will most likely appear in a future version of Windows 200x, and will probably be enhanced based on feedback from developers.
Object Pooling – Object pooling is an automatic service provided by COM+ that enables you to have instances of a component kept active in a pool, ready to be used by any client that requests the component. Once the application is running, COM+ manages the pool for you, handling the details of object activation and reuse according to the criteria you have specified. In order for components to be pooled, they need to be stateless, have no thread affinity, and be aggregatable. This means that VB components cannot be pooled, since they have a thread affinity, while properly written C++ components can.
Microsoft Component Services can function as a transaction manager for components. We will look at transactions in detail in Chapter 19. In managing a transaction for an application, Component Services will examine the components participating in the transaction to see what their transactional requirements are. Some components are quite happy to ignore whatever transaction is going on, while others want to or need to participate in a transaction. When a component is developed and deployed, the developer will set this transaction parameter for the component. Component Services will use that information to determine how the component should participate in the transaction, if at all.
In addition to managing components in their interactions with transactions, Component Services can also manage the components themselves. While this functionality was part of MTS, it was generally not what came to mind when looking at the features of MTS. Component Services uses the context of a COM+ component to help manage it during its lifetime.
A context is a set of run-time properties maintained for a collection of one or more objects. Each object is associated with precisely one context during its lifetime. Multiple objects can run within the same context and multiple contexts can reside within the same COM apartment. Context properties allow Component Services to provide run-time services. These properties hold state that determines how the execution environment will perform services for objects within the context.
Component Services provide a number of security features for your COM+ components as well. There are automatic security features, which are available without adding one line of code and are configurable administratively. Other security features can be integrated directly into the development of the component. Role-based security, which can be implemented programmatically or administratively, is the central feature of COM+ security. It allows for security down to the method level of a particular component, allowing all users to access a component, but restricting certain methods of that component to certain users.
The bottom tier of our three-tier architecture is the data access layer. This layer is responsible for integrating with the data sources that our application needs to be able to function. These data sources could be SQL Server or Access databases, Exchange message stores, MSMQ message queues, or UNIX legacy applications. They could exist on the server itself, on some other server on the LAN, or somewhere across the Internet. The data component is not only responsible for encapsulating the access to the data, but also for making the location of that data transparent to the application as well. All that the application needs to do is instantiate and use the component – the component itself figures out the rest.
Why use them?
There are a number of reasons why data-centric components are necessary portions of a three-tier, and therefore a Windows DNA application. Obviously, without a data layer, we would only be left with a two-tier application. But seriously, there are many reasons why encapsulating data access within a component leads to a more robust application.
It shields the developer from the inner structure of the database
This encapsulation is a primary tenet of object-oriented design. If the internal workings of a component are not exposed to the developer using the component, then these inner workings can be changed, updated, enhanced, or replaced depending on the changes to the physical data store below them. It also means developers cannot circumvent security or procedures by changing rows in the database in an incorrect order, etc.
It provides consistent data access to different data sources
By encapsulating the access to disparate data sources in a common interface, developers can use similar methods to access data regardless of where the data actually resides. This means that access to data stored in a SQL Server database can be accomplished using the same methods as data stored in a flat file on a UNIX system.
It makes the location of the data transparent
With the method to physically access the database encapsulated within the component, the location of the data does not matter to the user of the component. They merely access the component, and the component makes the necessary connections to the data source to retrieve the data.
The middle of our three tiers is the business component layer – sometimes referred to as the business logic layer or the application layer. No matter what it's called, its job is to provide the functionality for the application. This could mean managing a shopping cart for an e-commerce application, validating the benefits selection of an employee on the HR section of the intranet, or calculating the best route between two locations in a mapping application.
A business component is designed to hide the complicated interactions that a set of business rules need in order to process and also to shield the user interface designer from having to know anything about the underlying data. They simply interact with the methods of the business components to present information entered by the user and interpret the results of the component's processing.
Why use them?
Whereas data components are generally the most commonly used of the three component types in the n-tier architecture, there are also a number of compelling reasons to use business components as well. Some of these reasons are similar to, or even identical to, the reasons for using data components:
Business Rule Encapsulation
By encapsulating business rules inside a component, the developer using it does not need to worry about how the rules are actually processed. The component has a set of well-defined interfaces that the application developer will interact with. What happens inside should not matter to them, as long as the component delivers the proper results.
By encapsulating business logic within components, it makes it much easier to reuse that business functionality in multiple applications. For example, a business could have one component to calculate commissions on sales. This would allow any application that needs to calculate commissions to do so using the same component. All commissions would be calculated in the exact same way, since the same component was used to calculate it.
In developing Web applications, it is very straightforward to use script to create the business rules of the application. This makes it easy to get an application up and running quickly. However, scripting not only violates the first two reasons that we have already mentioned, but it can also cause performance problems as well. Since script is interpreted every time it is run, and a component is executed from compiled code, the performance of the script cannot begin to match the performance of the compiled component.
User Interface Components
The final layer of our Web application is the user interface layer. It is in this layer that the information generated by the middle tier of the application is presented to the client. How it's presented is generally based on a template in HTML or DHTML to which dynamic information is added. In some instances, the business logic may even select which presentation template to display too.
There is a lot of debate about the encapsulation of the user interface creation code into a component. Since ASP script can be embedded within an HTML page, it is very straightforward to use ASP script to insert the dynamic values into an HTML template. If that information is part of a compiled component, then it makes it very difficult to change the presentation without recompiling the component.
Why use them?
There are a couple of occasions where a user interface component could make sense for an application. They are generally related to making repetitive tasks easier for a developer, such as displaying a menu, or where script alone cannot generate sufficient performance. For each one of these reasons, there are other, non-compiled, methods that can be used to nearly duplicate the capabilities of the component-based UI, without many of its drawbacks:
There are usually certain parts of a Web application that are repeated in a number of different pages – possibly with different information. A frameless Web site that is trying to replicate a framed look-and-feel generally has to recreate parts of the page over and over again. This presentation could be encapsulated into a component, which would make implementation by a developer quicker and present a more standardized appearance. However, similar functionality could be accomplished through the use of an include file containing methods capable of rendering the information to the browser.
Performance is always a big issue when it comes to building scalable Web sites. The more performance we can ring out of any aspect of the application, the better the application as a whole will perform. Encapsulating complicated presentation logic within a compiled component will allow the site to perform faster, but at the trade-off of limited flexibility to change the presentation without recompiling the component. Another method, which was covered in Chapter 3, is to store repeated HTML blocks as strings in script, and then store that script in an application-level variable. This gives the performance of compiled code without sacrificing the flexibility of ASP and HTML integration.
Component Application Design
In building a component-based application, we need to look first at how to design an application that will use components. This will include both a process for breaking the application down into its components as well as how to design the actual components themselves.
Once we're comfortable with that, we must carry on to look at several issues related to building component-based applications for the Web. Specifically, we must address the design of our components and the interfaces we choose to expose in them, how we will tie out components together and the tools we'll use to implement all of the above.
Moving to Components
As we begin to move our application from a traditional monolithic or client/server application, we need to first look at how to break the application functionality into components. This is known as decomposition and can be done in a number of different ways. There are many books available on various object design methodologies, so we won't go into a detailed discussion about them here. The key thing to look for in selecting, or creating, a methodology is that it fits in with your manner of doing business, and also does not radically alter your existing development processes, unless you are not happy with the way things are being done now.
There are many advocates for each methodology, but it is up to you as an application designer and developer to select the one that best meets your needs. And if you can't find one that you like, then you should feel free to take parts of existing ones and create your own. As long as you can deliver a design that not only allows you to create an effective application, but also provides a roadmap for others to understand your application, then you have an effective design methodology for yourself.
As we begin to decompose our application into components, the first step is to partition the functionality of the application into the three tiers that make up a component-based application. A good way to begin is to look at each part of the application, and determine which tier it belongs in. If you're finding it difficult to select a particular tier for a piece of the application to fit into, chances are you're not splitting it up into small enough pieces. At this point, it is time to decompose the element you are currently looking at into multiple parts, with the goal that each part will fit nicely within the presentation tier, business logic tier, or data tier.
As these components begin to fall into our design, we still need to maintain some semblance of an application in our minds.
If we don't remember to do this at this time, then when it comes time to wire the components together at the end, the chances are we will have strayed from the application design as a whole. But by keeping in mind what the final goal of the design is, then the components we end up with will readily tie together into an effective application.
The application design is important in building a component application. Without a specified set of application requirements, it will be very difficult to create the components to support the application. In designing the overall application, there are a number of methodologies that can be used. For example, in the Use Cases approach, the application design is created by defining how the application will be used in specific instances.
Whichever method is used when we are designing a Web-based application, there are a different set of challenges that must be overcome that are not present in a traditional application. First and foremost, we as application designers have limited control over the tool that a person is using to access the application. Unless we have a tightly controlled intranet application, the chances that people will be accessing the site using different browser types and versions are quite high. This means that our application has to be designed to support this wide range of presentation types.
Internet-based applications are also usually accessed through a slow connection, such as a 33.6k or 56k modem. This has two primary implications in the design of the application:
First, the file size of any graphical images, including those images that make up the user presentation tier, must be such that they can be transmitted quickly to the client. An application will quickly lose its usability if the person using the application is continually waiting for the application to download images. Images in a Web-based application are fine, and provide many great benefits, but if they negatively affect usability, then another method of communicating graphical information to the user must be found.
The speed of the connection also means that every interaction with the server is going to take some perceptible length of time. This pause between interactions will have an effect on the perceived usability and performance of the application. If this delay becomes too long, or happens too frequently, then the performance of the application will be more negatively perceived. Thus, as the designer of a Web-based application, you need to be aware of the number of "round-trips" that you make between the server and the client. A system running solely over a higher-speed intranet can survive a greater number of "round-trips" than a system that relies on the public Internet in order to function.
Designing Components for the Web
In addition to some overall design concepts for Web applications, there are also some specific component design criteria that should be followed to produce an effective Web application. As applications on the Web need to be able to scale to handle large numbers of users, without causing a corresponding decrease in performance, the components used to build the application need to be able to support these requirements as well. With a Web-based application, there is also the challenge of creating an application that has the concept of a "user session," where a series of discrete user requests are treated as a single interaction with the application. Though there are multiple tools for maintaining user sessions, such as the Session object, the application designer needs to understand how the use of these tools affects the performance of the system.
If you go back and look at the basic definition of an object, it is generally given as a set of code and data treated as a single unit. By combining the data with the code of the object into a single package, multiple instances of the same object can exist at the same time, and can be used to represent different pieces of information. This is a very effective design for client/server applications, where an application can create objects that represent a specific entity within the application, and then through the interface of that object, have it perform the functions of the application. Since the object itself knows about the data it contains, it can perform these functions without the need of another source to provide the data.
This also means that a particular instance of an object is explicitly tied to the data that it contains. There is no way for that object to represent itself as holding different information if it needs to participate in a different interaction. The only way for that to happen would be for the system to create a new instance of that object and load it with the required data to perform the interaction. The strength of object-based applications is that the application simply knows that it interacts with a certain type of object – the specifics about the information contained within the object is of no concern to the application. The drawback is that we now need two objects to represent our two different pieces of data.
In applications that are supporting a single user, or a limited number of users, the creation of new objects to represent new items of data will generally not degrade the performance of the system. But when we start dealing with Web applications, that are supporting hundreds or thousands of simultaneous users, the creation of multiple objects for each user will have serious effects on the performance of the system. Luckily, our n-tier model for Web-based applications affords us a unique opportunity to address this issue.
Since each request that a client makes of a Web server is treated as a unique connection, there is nothing binding the server to the client in between requests. This means that while the user is viewing a fully downloaded page of the application in their browser, even if they are interacting with information on that page, the server does not have to perform any processing for that particular user during that time. It is free to go off and work with other users.
But what if the first user has created some objects that are uniquely linked to that user? The server has to make sure that these objects are maintained so that the next time the user comes, they will be readily available for that user to use. This means that server resources will be tied up just waiting for the next request to come in. If you extend this out by hundreds or thousands of users, you quickly see a lot of objects sitting around, chewing up server resources, waiting for the next request to come in. So what is the solution?
If a component can be designed so that it can perform the same type of work, but yet doesn't need to maintain any information within the component to perform the work, then it could be discarded after every use. Or if the component could be developed such that COM+ could pool the component, it would just be returned to the component pool. There would be no need to hold onto a reference to that object while the user is off working with the client page. The resources that were being consumed by the object can be returned to the server. By quickly freeing up resources in this manner, the number of users that the application can support at any one time will grow dramatically.
If a component does not carry any information around within itself, it is said to be a stateless component. A stateful component therefore is one that is said to hold information internally from one client interaction to the next. There are a couple of ways of making a component stateful in your Web applications. Conversely, by not doing these things, a component can be made stateless, and thus more scalable:
Saving a reference to a component from one page to another, by storing its reference in a Session-level variable, will cause a component to hold state.
Setting a series of properties in a component, and then calling a method to perform some interaction, will cause the component to hold state. Passing all of the parameters to process a method as parameters will help to minimize the statefulness of a component.
Another design criteria that we need to worry about when designing Web applications comes from the inherent stateless nature of the Web. As we mentioned earlier, once a server completes a client's request, it forgets all about that client. This means that as far as the server is concerned, that client has no past and no future with the application. Generally, an application consists of a series of steps that must be linked together in order to fulfill its tasks. If the server keeps forgetting about the client in between each step, it makes the design of the application that much more difficult.
When the application platform only supports sessionless applications, as a Web server does, we have to maintain the state of the session elswhere. There are a number of facilities that can do this for us, but each one has its effects on the scalability of the application. Step number one however is for a client to identify itself to the application each time it makes a request.
This identification could take the form of a cookie passed from the client to the server, a hidden FORM element that contains a unique identifier, or a parameter appended to the URL when the request is made of the server. In any case, this number must correspond to some information on the server that is unique to the client. This number can be a reference that points to an object with all of the session information in it. It could be a primary key into a database table that is holding the session information. It could also be an indication that there is other information in the request from the client, either in form fields, URL parameters, or cookies, which contain the state of the current session.
To put it succinctly, the three ways to maintain session information are:
Storing data in the Session object
Storing data in a database
Passing information between pages via the Request object each time a new request is made
Each of these methods has its advantages and disadvantages. The method that you choose for your application should be based on which has the least impact on the performance, scalability, and usability of the application.
When information is stored in the Session object, it can be quickly retrieved, but it means that the information is local to that particular server only. If the application is being scaled through the use of a Web farm, then this session information will only exist on one machine within the farm. If a user wants to access their session information, then they must be directed back to that same server each time. As this prevents balancing the usage of the server based on load, it can lead to some inefficiencies in the scalability of the application. On the other hand, accessing and using the Session object is quick and it is a well-documented method of providing session information.
Storing information about a user session in a database means that multiple machines within a Web farm can access it. In this way, the client is not tied to a particular server and the Web farm can operate much more efficiently. The drawbacks of this method are that the retrieval of information from the database is orders of magnitude slower than retrieving information from the memory space on a server. There is no way to store an instantiated object within a database, as you can do in the Session object. Finally, there is no easy-to-use interface like the Session object through which to access the information.
When you use the client to store session information, you are removing the burden from the server having to maintain that information. By reducing the processing requirements of the server, you lessen the number of resources it requires, which in turn increases the number of users the server can support with the same resources. The drawbacks of this method include the lack of common interfaces to handle information passed in this manner, the strain put on the network connection in requiring that this information be passed back and forth during every request the client makes of the server, as well as relying on the client to pass you information that is vital to your application functioning correctly.
In choosing the method for maintaining the session state of an application, you need to examine each of these possibilities and choose the one that best meets the needs of your design. As with any system, you can also choose to use a hybrid that combines elements of these three choices. For example, if one part of your application is best solved by having a stateful component used by two pages, then a reference to that component can be stored in the Session object to pass it from one page to the other, and then destroyed once it is no longer needed. Note however, that you will need to abide by the component design issues involved with component scope that will be examined in Chapter 15. Meanwhile, all of the other state information about the application session could be being stored in cookies and hidden form fields. In this way, the three different methods are complementary, rather than exclusive.
There are two key aspects to the design of a component. The interface of a component is how it communicates with the outside world, and is the only way that the outside world can work with the services provided by the component. We also need to look at where the component is physically located, which will determine how it can participate with the other components that comprise the application.
The Component Interface
The component interface is the mechanism by which the component interacts with the rest of the world. It is designed to provide access to the information and the functionality supported by the component. The interface is the only way that other components or applications can interact with the functionality within the component. The interface is comprised of the public properties and public methods of the component. Any private properties or private methods of the component will only be accessible from within the component, and therefore cannot be access via the component's interface.
We will be looking at COM and interfaces in much greater detail in the next chapter.
When designing a component's interface, we need to support the other aspects of the component design that we have already looked at. Namely, the design of the interface will directly lead to the statefulness or statelessness of the component. If we create a component interface that relies on properties being set before the component can perform any functions, then we are creating a component that is holding some internal state for some length of time. It will also require multiple calls into the component – some to set the properties, and then one to execute some action. You can see from this code sample the number of times we would have to call into the component:
<% Dim obj As Server.CreateObject("MyComponent.Test") obj.Property1 = 123 obj.Property2 = 234 obj.Property3 = "abcd" Call obj.Method1() Set obj = Nothing %>
If we were to design this component interface such that the method call carries with it the values that it needs to perform its function, then we will have a much more stateless component. There will only be one call into the component to process the function, and no internal information will be stored within the component. If we created another method called Method2 to achieve that, a call to it might look like this:
<% Dim obj As Server.CreateObject("MyComponent.Test") Call obj.Method2(123, 234, "abcd") Set obj = Nothing %>
As you can see, the new interface only requires one call into the component before the component can be released. This is in comparison to four calls into the component in the previous example before it could be released. And in terms of the effect on scalability, the sooner we can release a component, and let the system have its resources back, the more scalable the system will become.
In addition to the interfaces that a component supports, the physical location of the component can also affect the scalability of the application. As our application interacts with its components, it will want to do so in the fastest and most efficient way possible. Any time wasted in creating a communication channel with a component will be more time during which the performance of the application is slightly worse than if the component was not being used. You always need to remember when trying to make applications highly scalable, to take any increase in processing time or increase in resources usage and multiply it by at least the projected number of concurrent users. Once you do that, it becomes plain to see how a minute change in resources usage or time can begin to cause serious problems.
We want to make sure that the interaction between our ASP scripts and our components, and between our components, happens in the quickest and most efficient way possible. This generally means that the component should run within the same process as the ASP application. In the next chapter, we will look at how COM+ enables you to administratively change where a component actually executes. As with all potential speed improvements, running your component in the same process as your ASP application, while faster, also makes it more likely that a problem with your component could affect your entire ASP application. When a component is run within the same process as the ASP application, then all calls into the component will be done using direct function calls. This is the fastest way to access the component.
If we run the component in a different process, then any information that is passed between the component and the application must cross the process boundary to get from the application to the component. In the Win32 environment, this is a resource and time intensive operation. It will take more system resources to make the call, and it will also require more time. This happens in both directions in the conversation between object and application as well. So any information, such as method parameters, that is passed from the application to the object will have to be marshaled across the process boundary. On the return trip, any information that the object wishes to send back to the application, such as a return value, will need to be marshaled as well.
We can also utilize the DCOM capabilities of the Win32 environment to execute our components on a different physical system than the one running the application. Even though this connection happens transparently to the application, there are still major performance penalties to be paid. Everything that was mentioned about accessing components running in a different process holds true with DCOM components but more so – the time it takes for client and server to communicate is greatly increased. This is especially true as latency increases when you move off of a LAN and onto a WAN or the Internet. You need to be very careful if you want to use DCOM components in your ASP application as a result of this.
Tying the Components Together
Once you have constructed all of the components for your application, you need a way of tying these components together. Without some sort of glue that tells the components how to interact with each other, you really don't have an application. You can use other components to serve as the glue that holds your components together, but in practice this makes constructing an application as difficult as constructing a component. One of the advantages of a component-based application is that you can quickly wire together a set of components to create your application. So to facilitate this, we can use ASP scripts as the component glue in our application.
On the system that is hosting your application, you will naturally need Active Server Pages installed. The system with ASP installed on it now becomes more than a Web server – it becomes your Application server. And, in order to serve your Web applications to clients, it will need to access the components that you need to build the application.
There are two types of components that can be used to build an application. Those you have custom-built and installed on the server for the application and those components already existing on the server, be they by Microsoft, yourself or some other third party.
Reusing Existing Components
When reusing existing components, you must first make sure that your use of the component does not break any other applications that are relying on the component. For example, if you were to update the component to a newer version to support your application, you could potentially be breaking an existing application that was relying on a particular version of the component. Also, if the component relies on registry settings to hold its configuration, and you change those settings to suit your application, then other applications using the component may no longer function. Note however, that none of the above should happen if you follow basic interface development guidelines.
The easiest way to see which other ASP applications are using the component is through the use of a simple text search routine, such as GREP, or the Find in Files function of Visual InterDev. Since all ASP script code is in a text format, you can simply search through all of the .asp files on your application server for the ProgID of the component that you are going to reuse. This will help you directly pin-point any applications using your component. You can then make provisions to test those applications when you make any changes to the component's configuration.
Installing New Components
There are a few ways to install newly created components onto your application server.
One option is to take the file containing your component – typically a .dll file – copy it to the server and then register it with the operating system. By doing so, sufficient information is place in the system registry to allow applications to reference the components and have the system know which component it's referring to. Installing components in this way, however, does place the onus on you to make sure that all the files the component depends upon are also present on the server. Without them, the component will be unable to function properly, and could possibly not even be registered properly.
A second way is to first build an installation program on the development machine, copy it to the application server, and then execute it. This is usually a foolproof method of ensuring that the dependencies for the component are installed properly alongside the component. One downside of this method, though, is that it requires a physical interaction with the console of the Application server. Also, if the installation program does not take into account the proper versioning of components, it can very easily overwrite a version of an existing DLL with a version that causes other applications to not execute properly, or even cause the server to crash.
A third method is through the use of a COM+ application. A COM+ application, as we mentioned earlier, consists of a set of components that the Microsoft Component Services manages as a single group. In Chapter 15, we will take a look at both how to create a COM+ application, and how to move that application from one application server to another.
Building your Components
All this talk about building components to encapsulate business logic or data access logic has hopefully laid the groundwork for you to build your own components. Well, the first thing you need to know is what tools you can use to create your components. That is one of the strongest points about COM – it is language-neutral. This means that you can create your components using nearly any language you choose. And not only are you free to select a language, you are also free to choose from a wide variety of tools with which to write your components.
There are many development tools available on the market for you to create components with. The list includes:
Microsoft Visual C++
This tool creates the highest performing, lightest-weight components possible. By utilizing the Active Template Library, some of the intricacies of component creation are taken care of. But all of this performance comes at a price. This is generally considered the most difficult way of creating components and has the steepest learning curve. We will take a look at using Visual C++ in Chapters 17 and 18.
Microsoft Visual Basic
At the other end of the spectrum, Visual Basic is one of the easiest tools to use to create components. These components don't necessarily have the optimal performance that C++ components do, nor do they have their flexibility. That being said, most benchmarks have shown that a VB component, when designed and used properly, performs at about 90-95% the speed of a C++ component. What they do have is the ability to be created quickly and easily by a wide group of developers. We will be looking at creating components in Visual Basic in the next chapter.
Windows Scripting Components
Microsoft has extended the world of component creation to scripting languages as well. By adding an interface definition written using XML, we can now create components using the exact same tools and code that we use to create our ASP scripts. We'll see more of this in Chapter 16.
After creating a component, the most important step prior to using the component is testing it. A component needs to be tested not only to ensure that it is performing the operations that it was designed for, but that it is not adversely affecting the rest of the system. There are three types of testing that you can perform on the component.
Functional Testing – This type of testing ensures that the component works as advertised. Testing of this nature is generally done by applying a set of inputs to the component for which the proper outputs are already known. The outputs generated by the component are compared with the known outputs, and any discrepancies are resolved by changing the functionality of the component.
Stress Testing – To make sure that the component is not adversely affecting the systems resources, it can be tested under stress. This is usually done with a testing tool, such as WAS or WCAT, which simulates a large number of users accessing the component simultaneously. During this testing, the system resources are monitored to ensure that there is not any excessive resource utilization of the component.
Integration Testing – Integration testing looks at how the component interacts with the other components that are going to be making up the application. This is done to make sure that there are no issues, such as contending for the same resources, or relying on different versions of the same DLL in order to function.
Once this testing is complete, the component is then ready for installation onto the Application server, and then for integration into the application.
An Application Design Case Study
In this chapter, we have looked at a lot of theory concerning the creation of n-tier Windows DNA applications. This theory should have covered all the bases necessary to create a component-based, scalable Web application. The problem with just theory is that it is just that – theory. It is the practical examples that the majority of us developers like to work with. Yeah sure, give me the theory, but enough already and show me how it's done!
In this section we will do just that. With all of the theory on our plate on creating a Windows DNA application, let's put it to work to actually build one. Well, that's a bit presumptuous, as we still need to look at actually coding the components of the application. So, in this section, we will see how to design a Web-based Windows DNA application:
First, we will define the problem that we are going to create the application to solve. This is generally the most important, but usually the most neglected step in the design phase.
After defining the problem, we will look at the design of the application. This design will include the component architecture and the interfaces presented by the components in the application.
Finally, we will look at any design tradeoffs that we were forced to make and how these tradeoffs will affect the scalability and extensibility of the application.
Defining the Problem
WhizzyBang.com, like all small but growing Internet companies, is dealing with the administrative hassles of continually adding new employees, employees changing roles and titles, and people spread throughout the world. These challenges have made it difficult for WhizzyBang.com to maintain a working human resources support system without driving the poor HR employees crazy. They have decided to stop being cobbler's children, embraced the Internet as the way to do business and created a Web-based human resources information system.
This human resources system will initially need to support the following functions:
The hiring manager is responsible for the initial creation of the employee's record.
The human resources manager must validate the information and process the proper government paperwork for the employee.
The employee will need to use the system to select their health care benefits. This can be used by either new employees or by existing employees.
Since the information in this application will be used by other systems as well as the human resources system, it will need to be stored in a centralized system. Other information will be required to be sent to certain agencies outside the company. The data being used by the application is stored in the following systems:
The employee database is stored in a SQL Server 7 relational database.
The health care selection information is stored in an Access database.
The government paperwork is delivered to a processor via an XML-formatted e-mail transmission.
The users of this application will be located in locations throughout the country/region. They will all have Virtual Private Network (VPN) access to the corporate network, but this connection is only over a 128k ISDN line between the data center and the VPN provider. A VPN allows the users to dial up to the public internet and then tunnel through the corporate firewalls to reach servers within the company. The VPN is configured to allow only the HTTP protocol to pass.
The systems to run the application will be hosted at a centralized site. These systems will be dedicated to this application, but the databases that the application will access will be on separate systems. The users of the application will have Microsoft Windows 98 and 2000 workstations. The application will only be accessible when the workstations are attached to the corporate network. The application should always check for the latest version when executed by the user.
Now that we understand the high-level requirements of the application, we can take a look at how some of these requirements will affect the design of the application. This is critical to examine at this point. If we were to design the application, without looking at the implications of the requirements on the design, then we could design an application that was difficult or impossible to implement or use.
In looking at these design implications, we will want to understand certain aspects of the implications. First, we need to know if there are certain requirements that are mutually exclusive to one another. For example, if one requirement states that the system must be accessible by disconnected laptop users, and another states that only HTTP access is allowed, we have a dilemma. Since HTTP access is only available as a connected protocol between a client and Web server, we have two requirements that are in conflict with one another. The requirements will need to be changed in order to support the realities of the system.
In looking for design implications, we will also look at those aspects of the requirements that are very precise, such as the type of database used by the employee database. These very specific points must, by definition, be carried directly to the final design. Those requirements that are more general, such as the types of workstations used by the clients, allow us more flexibility in the design of the application.
In an initial pass at the requirements, the following can be identified as design implications:
The workstations will support a Win32 application, but the requirement that the application be self-updating implies that a browser-based application will be easier to support. In a browser-based application, the latest version of the application is always available by the client.
The databases that the system will access both have OLE DB providers, so we can utilize ActiveX Data Objects to interact with the database.
The human resources manager provides a validation step to the creation of a new employee, so there needs to be a notification mechanism to tell this user that a new employee is in need of validation.
Since the same application will be used for both new entry and update of benefits information, it should support both a guided and interactive interaction with the data.
By recognizing these implications, we can guide the design towards the path of least problems when creating the application, and away from choices that are impossible to implement. It also makes the actual design process less fraught, more straightforward and more efficient. We can always refer back to these implications, as well as the requirements, to ensure that our design is moving along the correct path.
Now that we have established the application's requirements, and taken account of the resulting implications, we can now get to work on designing it. At this point we have a number of directions in which we can proceed with our design exercise. There are almost as many application design methodologies as there are books on Active Server Pages. The design methodology that you choose to work with may be one that is thrust upon you by your employer, one that you have used in the past, or one that you have looked at and seems interesting. However you select a design methodology, and whichever one you select, you will eventually end up at the same point – the design of an application.
You should take into account the size of the application development effort when choosing a design methodology. Are you going to be the only developer working on this application? Will you or another developer ever have to extend and enhance the application? Does your application have to integrate at the component level with other applications? These questions and others need to be asked so that you can determine if the methodology you are using is compatible with the environment your application has to work in.
For this example, we will not subscribe to a particular published methodology, such as UML or Use Cases. As a lot of developers have found, the published methodologies are great in theory, but when it comes down to actually creating an application design in the real world, the methodology that works best is usually a hybrid that meets the unique needs of the application. In our application design methodology, we will be concerned with five aspects of the design:
Component Architecture – How the components in this application are partitioned across the logical tiers, and how these components work with one another.
Data Tier Interface – This describes both how the data components logically integrate with the physical data stores as well as how they are accessed by other components in the component architecture.
Business Tier Interface – This describes how the presentation tier accesses the business components and which data tier interfaces each component works with.
Presentation Logic – This describes the various visual interfaces that the user will be interacting with in order to perform the functions specified in the application requirements.
Integration Architecture – The integration architecture specifies how the components of the application will be tied together. This will cover the physical location of the components, the packaging of the components into applications, and the mechanisms that the components will use to communicate with one another.
The design methodology is primarily concerned with the interfaces of the components of the application, and how these interfaces are tied together to create the application. The interface of a component specifies the information that the component will provide based on inputs provided by a caller. The internal workings of a component are left up to the developer to implement. As long as the component interacts with its interfaces in the way specified in the design, the component can be considered a black box. With components designed in this way, the primary integration task is to tie the components together, which is the final step in the design.
The component architecture of the WhizzyBang.com human resources application will define the components that make up that application, as well as how these components are tied together. The component architecture provides the glue between the presentation layer and the actual data. In this diagram, we can see the role that the component architecture will play in the application:
The component architecture in the application represents the business logic components as well as the data access components. These components will have their interfaces defined later, so for now we need to decide the various objects to create. Generally, object decomposition will allow you to begin with a loosely defined object and break that object into its component objects until you reach a more discrete size. First, let's take a look at the data objects.
In this application, there are three primary data sources. These three data sources are the SQL Server employee database, the Access benefits information database, and an e-mail box for sending tax information. The SQL Server database is unique in that it is used for both the retrieval of information as well as the storage of information. The employee's information is initially created by the application and stored in this database and then it is retrieved and used in the application. The information in the benefits information database is used in a read-only capacity by the application. There are no requirements in the application to write information to the benefits database. The tax information repository is exactly opposite, in that it is a write-only data source. This means that once information is written to that data store, it can no longer be retrieved by the application.
These three primary data sources can correspond initially to three objects. To begin with, we can refer to these as:
The Employee component
The BenefitsInfo object
The TaxInfo object
The Employee component will be responsible for the interaction with any data inside of the employee database. Since this database contains different types of data, such as name and address information, dependent information, and benefits selection, it probably makes sense to have multiple objects to access this single physical data store. The process of breaking the Employee component up into a more granular set of objects is known as object decomposition.
In our example, we will decompose the Employee component into three separate objects. We will also choose an object-naming schema that will relate these three granular objects back to the original Employee object. The name and address information will be accessed via the EmpInfo object. The dependent information via the EmpDependent object, and the benefits information via the EmpBenefits object. As the BenefitsInfo object and the TaxInfo object are already somewhat granular in their nature, we will not decompose these objects into multiple objects.
The business tier of the application needs to deal with the creation and validation of an employee record and the selection or modification of employee benefits. The employee information creation section needs to enforce certain business rules on the information to ensure that the information is added correctly to the database. In this case, we can see the separation of functionality into the data object being only responsible for the access to the information, and the business logic serving as the enforcer of the business rules.
We will create three primary business objects to work with the application. The NewEmployee object will process the business rules related to the creation of a new employee record by the hiring manager. The EmpValidation object will be used by the human resources manager to validate the information related to the new hire, and then to process the proper taxation information. The final business object will be the Benefits object, and it will be responsible for enforcing the business rules regarding benefits for the employee.
As you can see, we have closely related the business object composition to the required functionality of the application. This allows for a closer interaction between the business rules and the presentation logic, which will reduce the number of objects needed to process a given part of the application. By reducing the number of objects that are involved in the application, we can increase the scalability of the application, since fewer resources will be needed by each application session.
You can now see that the business component architecture that connects our presentation logic with our physical data storage contains a set of eight objects. In the next two sections, we will apply the interfaces to the objects so that they can communicate with one another, and with the rest of the application.
The Data Tier Components
The data tier components are comprised of the interfaces of the five data access components. These components provide access to the data used by the application. The data interface will determine the methods in which the business components will interact with the data components to both retrieve and set information.
These components will also have an interface to the physical data itself, but the definition of this interface is not critical to our application design. The constraints of the methods of accessing the physical data, along with the specific information required to support the data tier interface, will determine how this interface to the physical data store is actually created. This physical interface is also driven by the system that the data is stored in. For the database information stored in the SQL Server employee database, this information will be accessed using the SQL Server OLE DB provider. The data for the benefits information, stored in an Access database, will be retrieved through an ODBC connection. Finally, the data transfer requirements needed by the TaxInfo component, since this data is transmitted via e-mail, will be performed using an SMTP mail object, like the Collaboration Data Objects (CDO).
The external interface for these components is important in that this is the manner in which the object is accessed by the rest of the application. Once this interface is defined, the business objects that need to retrieve or set business data can call through these interfaces to interact with the data that they need. The business objects will in turn expose an interface to the presentation logic.
For our five data objects, we will define the following methods to be available through their public interfaces:
GetBenefitsForClass, GetBenefitInfo, GetBenefitOptions
AddBenefit, CheckBenefitStatus, GetBenefitList, RetrieveBenefit, UpdateBenefit
AddDependent, GetDependentList, RetrieveDependent, UpdateDependent
CreateEmployee, DeleteEmployee, EmployeeStatus, EmployeeClass, IsEmployee, RetrieveEmployee, UpdateEmployee
These interfaces are defined to provide access to information stored within the data sources of the application. All will be through these objects' methods. If a business object, or a user presentation, needs to interact with the data sources to access information for display, or to update information, it must be through these methods.
The Business Tier Components
Now that we have defined how our application will be able to access data, we need to define the components that will allow the presentation logic to get access to the data in a way that is consistent with the business rules of the application. We have defined three business objects for this application – NewEmployee, EmpValidation, and Benefits – which correspond to the primary functional requirements of the application.
The interfaces for these components will be called by the presentation logic and will need to retrieve all the information needed for presentation by the user interface from the data tier. It will also need to define a way to update that information if it is changed by the user. The internal workings of these components will be responsible for implementing the business rules of the application. For example, it will be the responsibility of the Benefits component to ensure that the employee's benefits selections are valid. As with the data objects that we looked at earlier, the actual inner workings of the component are not important to us – as long as they perform the business logic processing correctly.
The three business logic components will support the following interfaces:
AddBenefit, AddDependent, DeleteBenefit, DeleteDependent,
RetrieveBenefitList, SetBenefit, UpdateDependent
GetEmployeeInfo, GetValidationList, LoginEmployee, SetEmpTaxInfo,
Through the interfaces we have defined here, the presentation logic for the application must retrieve and set all of the information that it needs to function. The three-tier architecture that we have defined means that there is no direct access between the presentation logic and the data tier or the physical data itself. In this way, the presentation logic is shielded from any underlying changes in the data logic or the data structure itself. As long as the interface to the business components doesn't change, then the presentation logic doesn't need to be changed.
The presentation logic is the part of the application that actually ties into a user. This is the section of the application that generates the user interface and then receives input from the user interface based on the user's interactions. It is through the business component interface that the presentation logic communicates with the rest of the application. The user of the application will only see what is presented by the presentation logic – there will not be any direct interaction between the user and the database or the business objects.
The requirements for the application are such that the presentation layer of this application is going to be a Web application. Even though the capabilities of the client will support a Win32 executable application, there are other requirements that would make this a less-than-optimized solution. One of the requirements is for the application to always support the latest business logic. This would only be possible if every time the application requires business functionality, it requested that functionality from a central location. While this would be possible in a Win32 application that uses DCOM to communicate with components on the central server, again another application requirement prevents this solution.
Based on the analysis of all of the requirements, we have decided that the best architecture for the application is a Web-based application, where the user interface is presented as a series of dynamically generated Web pages. These Web pages will be created by a set of ASP scripts that will use COM to communicate with the business components. Each ASP page will generate one or more Web pages based on inputs by the user combined with information from the business components. The ASP scripts will be grouped into three categories – roughly corresponding to the three primary functional areas of the application.
The ASP scripts that form the presentation logic are categorized as follows:
New Employee Information
DisplayValidationList.asp, EmployeeTaxInfoForm.asp, SetEmployeeTaxInfo.asp, ValidateEmployee.asp, ValidateEmployeeForm.asp
ChangeBenefit.asp, DependentList.asp, DisplayBenefits.asp, EditDependent.asp, EmployeeLogin.asp, VerifyEmployee.asp
Now that we have defined the three layers of the application, we can take a look at how these three layers are wired together.
The integration architecture is where we tie the application together. This will specify how the interfaces of the business objects are called by the presentation logic, how the business components will call the interfaces of the data objects to get the information they need to process, and where the data components go to get the information they need. Once the integration architecture is set, the rest of the application simply becomes an implementation task. With the component nature of the application, as well as a well-defined set of interfaces, this development effort can be readily partitioned between multiple developers.
For our design exercise, we will not specify all of the integration paths, but we will look at a sampling for the application. The method for creating this integration architecture usually follows a relatively straightforward path:
For the presentation logic, define what information is needed to be displayed, or what information the user is returning.
For each piece of information, determine which business object and which corresponding interface is needed to either supply that information, or provide that data back to the application.
With each business logic method, determine the physical data interaction required, and utilize the interfaces of the data components to provide that data.
Finally, for each method of the data component objects, determine the physical data needed to support the method, and link that method to the required physical data.
For example, the SetEmployeeTaxInfo.asp page will need to take the taxation information entered by the human resources manager and send that information to the appropriate government agency. The business logic to perform this functionality is the SetEmpTaxInfo method of the EmpValidation object. This method will need to convert the tax information entered by the user into the appropriate format, which in this case is an XML format. It will then utilize a data object to actually transmit the information. The method to transmit this information is the SendTaxData method of the TaxInfo object. This method will need to take the information as submitted and open a physical connection to the data destination to transfer the data. In this case, the data destination is an e-mail box, so the physical connection is via an SMTP server.
This methodology of creating the integration architecture is carried forward through every presentation logic component in the system. As more and more of the component integrations are defined, the process simply becomes a wiring up of already defined components. Once this is complete, adding new presentation functionality that is supported by existing business objects becomes a very straightforward task.
As we have constructed the architecture of our application, we have made a number of decisions about the design that will affect other aspects of the application. These are known as design tradeoffs. A design tradeoff usually involves making a choice between two possible design directions. The inputs that go into making a design trade-off include the effect on the functionality of the application, the length of time it takes to develop the application, how well the application can scale, and how easily the application can be enhanced and extended.
In creating our Web-based human resources application, we are primarily concerned with two aspects. Since the entire company will use this application, we need the application to be as scalable as possible. This means that we should be able to support multiple simultaneous users, and we should be able to increase the performance of the application by adding additional server processing power. The other aspect we are concerned with is the extendibility of the application. Since there will undoubtedly be additional functions desired by users, we want to make sure that our initial design will be able to support these additional functions. This support should be made available without requiring an extensive redesign of the application.
So let's take a look at the design for our application, and answer these two important questions about extensibility and scalability.
Can it be Extended?
The extensibility of the application can be traced to the design of the object model. We need to look and see if there are other application functions that could be implemented using the existing method calls. For example, we have the necessary business and data objects to access and edit an existing employee's information. In this way, we can add some new presentation logic to the application and, using the existing business object, add the employee information editing functionality to the application.
If we take a look at the existing data object interfaces, and look for any new business functionality that can be supported by them, we can determine any other application functionality that can be readily added. For example, the data object interface already supports the necessary interfaces to retrieve benefit information for an employee. A new business object to allow the corporate health claims processors to validate an employee's health care claims against the benefits they have selected could access this data interface.
When faced with new application requirements, the application designer should first look to the existing objects that comprise the application. Rather than diving in and immediately creating new objects, it makes sense to first look at the existing objects. When looking at these existing objects, look for the exact interfaces that you need to support your business functionality. If they don't exactly exist, chances are there are similar interfaces that you can extend and enhance. Only when you have exhausted your review of existing interfaces, should you turn to designing a new interface. Don't forget either to look ahead and bear in mind the possible future enhancements your new interface could support.
Can it Scale?
The other design question that we want to answer about our application is whether or not it can scale. Earlier in the chapter, we talked about designing components for the Web. By utilizing these design principles, we can help our application scale upwards to support more and more users. The key aspects for the scalability of object applications are related to the implementation details of the component, as well as the level of statelessness of the components.
In order to create components that can be pooled by COM+, we will need to make sure that the components used in this application are created with Visual C++. This poses a big implementation trade-off in that it generally takes longer, and requires a different developer skill to create a component using Visual C++ than it does using Visual Basic. We need to determine if the scalability afforded by C++ components outweighs the greater development time and cost over developing components in Visual Basic.
The scalability of the application is also affected by the statelessness or statefulness of the components in the application. If the design of the components is such that they have a longer lifetime, then the system will need to create a greater number of components to support more and more users. If the components can be used quickly and then discarded, then the system can function with a smaller number of components active at any one time. Since all of our components marshal all of the information necessary to perform each method with the parameters of the method, and since no information is retained from one method call to the next, we have created stateless components, which will lend to the scalability of the application.
We have now created the application design for our human resources application.
The application should be built as a component-based three-tier Web application.
It will utilize a browser-based presentation generated by ASP scripts.
The ASP scripts will interact with a set of eight business logic components, which will support the processing of employee information, benefits information, and taxation information.
In order for these business components to interact with the data that they need to perform their functions, they can draw from a set of three data components.
Only these data components will interact with the actual physical data source, be it a SQL database or a SMTP mail server.
The entire application will be designed using components to support future extensibility as well as give the application the ability to scale to support a large number of users without requiring a redesign.
Web applications are quickly becoming the new application development paradigm for business in many industries. They are being used to build intranet sites to replace client/server or terminal-based applications. Consumers shopping for items on the Web are interacting with Web applications and companies that work together are now able to peer into each other's systems through a Web-based extranet. All three of these scenarios rely on the creation of Web applications that are easy to build, have robust capabilities, and are scalable to support large numbers of users. Windows DNA is the new application architecture for these types of applications for the Windows platform.
In this chapter we have seen:
How Windows DNA allows us to build applications that are scalable and flexible
What makes up a Web application and how you should go about designing one
How components and Microsoft Component Services make it easier for us to build Web applications
How you use the various pieces of the Web application architecture to create Web applications
The three types of components and why we should use them
A component-based application case study
In the next chapter, we will take our newly discovered knowledge about components and look at how to build a Windows DNA component using Visual Basic. Then in Chapter 15, we will then look at how to deploy it into a COM+ application, and test it to make sure that it works.
About the Authors
Alex Homer lives and works in the idyllic rural surroundings of Derbyshire, UK. His software company, specializes in office integration and Internet-related development, and produces a range of vertical application software. He has worked with Wrox Press on several projects including Professional Active Server Pages 2.0.
This book was also authored by:
We at Microsoft Corporation hope that the information in this work is valuable to you. Your use of the information contained in this work, however, is at your sole risk. All information in this work is provided "as -is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Microsoft Corporation. Microsoft Corporation shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages. All prices for products mentioned in this document are subject to change without notice.
International rights = English Only