Like Windows DNA, our architecture is completely based on the three-tier services model. As you can see in Figure 2-1, this model sees every application as a set of services, divided into three separate categories:
Figure 2-1. Our scalable architecture is based on the three-tier services model.
Our architecture supports the three kinds of user interfaces that dominate the Windows world of today. All of the user interfaces are graphical, but they're clearly distinct.
The three categories, shown in Figure 2-2, are
Figure 2-2. Our architecture supports three kinds of user interfaces.
Our countryman Ivar Jacobson1 introduced the idea of use cases as a means of capturing requirements. A use case diagram documents how a specific actor2 interacts with the system or system-to-be. This interaction should provide the actor some useful result, such as new information or a chance to enter new information—a customer order, for example—into the system. The example diagram in Figure 2-3 shows how actors of the Administrator class have two primary use cases, one of which is extended by four secondary use cases. The diagram also shows how customers will be able to browse races to find the information they need to place winning bets on horses.
Figure 2-3. Actors have use cases, allowing them to use the system. Secondary use cases, such as Maintain Horses in the diagram, can extend primary use cases, such as Maintain Resources.
You can read about use case modeling in Jacobson's book Object-Oriented Software Engineering.3 This book is the book on use case analysis, even if it doesn't really cope with recently deployed environments such as MTS and COM+.
Anyway, a use case begins when something happens in or to the business. Figure 2-4 illustrates this.
Figure 2-4. A use case often begins in order to take care of a business event.
A scenario is one way through a use case. Most use cases encompass several different scenarios. One of the jobs of an analyst is to discover the most important and usual scenarios of each use case. Many scenarios require the use of one or more business services. To take advantage of a business service, as Figure 2-5 shows, the user must connect to a business services object.
Figure 2-5. The use case often starts with a connection to a business services object.
Let's consider for a moment a use case in which the actor receives a customer order. An implementation of such a use case could look a lot like the diagram in Figure 2-6.
In Figure 2-6, the user interface object, residing in the user services tier, creates and connects to an order object in the business services tier. For each order line entered by the user, the order object creates and holds an order item object.
Figure 2-6. The user interface object connects to an order object, which in turn owns a number of order item objects.
Let's talk about object state. There are two kinds of object state to consider:
In our view, system state is best left to COM+ or to MTS. They do a very good job of managing system state for us. It's one of the three important contributions4 they make to us as application developers.
Figure 2-7. In a pure object-oriented environment, server-based business objects are responsible for keeping application state.
An object purist would probably like the diagram in Figure 2-7. The objects have application state, and they're fully responsible for it. An order object encapsulates all its order items in a collection of order item objects, and it's fully responsible for them as well.
Aesthetically, there is nothing to say against this model, but in practice it has a very important drawback. It doesn't scale very well. The order object owns all the order item objects, and they all need to stay in server memory as long as their client hangs on to the order object. Being in memory, these objects consume scarce resources. So the number of clients such a model can support is limited.
In some cases, you can't avoid such a structure, and in such cases you have to accept this drawback. In other cases, however, you can choose from other, less wasteful, solutions. In our opinion, you should prefer the less wasteful solutions even if, in the eyes of the object purist, they're less elegant.
As you'll see in the next two sections, state is either permanent or temporary, with each condition having its own suggested handling requirements.
Object purists tend to "think away" the database. It doesn't really exist for them. "After all," they say, "the database is just a place to spool your object state into." If they accept the database at all, they want it to be object-oriented rather than relational. They prefer that functions such as concurrency control and record locking be carried out by a business object in RAM rather than by a database.
We take the opposite position. We want to make as much use as possible of the functionality already present in modern database products such as Microsoft SQL Server, Oracle, and others. Their creators have had years and years to polish these functions, and they know all about transactions, record locking, and other data handling requirements. These are in the air they breathe.
This, by the way, is one of the reasons we've liked MTS so much from the first day we heard about it. MTS is very much based on this idea—that is, it makes maximum use of functions already present in database products and in the operating system. COM+ doesn't change that at all. Instead, it takes the ideas behind MTS to new heights.
Figure 2-8 shows parts of this idea. We want to push permanent state to permanent storage in the database as soon as the state is modified. As a matter of fact, COM+ and MTS more or less force us to do so. As soon as a user finishes and confirms a transaction and as soon as your MTS or COM+ business components have successfully sent the transaction to the database, the objects are automatically deactivated and (at least with MTS as opposed to COM+) actually killed.
The same goes for an unsuccessful transaction. Since it is unsuccessful, any changes already made in any database are automatically rolled back. When this action concludes, COM+ or MTS deactivates and kills every object that took part in the transaction.
Figure 2-8. Move permanent state to the database.
So there's no way for a transactional business object residing in COM+ or MTS to keep by mistake application state that's not consistent with the state kept in the database. The database, in fact, is the permanent keeper of state, and it knows very well how to protect its overall integrity.
Our advice is this: don't try to take the responsibility of keeping permanent state away from the database by putting it in the business tier! This strategy won't work nearly as well as allowing the database to serve as the only permanent state keeper.
Temporary state such as the modified name of a customer or a horse is best maintained by the client in the user services tier. Whenever you move temporary state away from the server and into the client, each client consumes fewer server resources. As a consequence, your server application will scale extremely well.
ADO recordsets can help you achieve this. An ADO recordset can be totally disconnected from the database, as described in Chapter 1, "Planning for a New Architecture." In its disconnected state, it can move freely between processes and even between machines. In this respect, disconnected ADO recordsets differ from other COM objects.
If you're not familiar with this kind of behavior in ADO recordsets, you should study the Visual Studio 6.0 documentation.
Figure 2-9 shows how you can send disconnected ADO recordsets all the way to Windows client applications as well as to DHTML clients that accept ActiveX components. In order to be able to receive ADO recordsets, DHTML clients need to have in place the mechanisms necessary for handling them. Those mechanisms are in fact ActiveX components, and they're included in the Remote Data Service (RDS) of Microsoft Data Access Components (MDAC).
Figure 2-9. In many instances, you can move ADO recordsets all the way to the clients.
If you want to know more about these ActiveX components, just visit www.microsoft.com/data, which is the Web site for Microsoft Universal Data Access, and visit www.microsoft.com/data/ado/rds to learn about RDS.
Figure 2-9 highlights the fact that sending recordsets all the way to the client helps scalability enormously. It allows you to deactivate server objects and recycle the resources they used, especially if the only reason for keeping them alive is to keep the application state of the object available to the user and the client application.
Only in the case of standard HTML clients, that is, HTML 3.2, are you prevented from sending disconnected ADO recordsets because standard HTML clients can't accept ADO recordsets. Normally, then, you must keep state on the server side and allow the objects to stay activated. You'll have to accept a lower level of scalability when supporting standard HTML 3.2 clients.
But the situation is changing rapidly. XML is a confirmed Web standard for structuring, keeping, and transporting data sets, and most browsers will eventually support it. Microsoft is investing huge amounts in XML technology, creating tools for building XML-based solutions. The first ones are already here with ADO 2.5 and Windows 2000. Many others will come, and you'll have good tool support when you build your applications around XML.
Figure 2-10 shows how all kinds of future browsers might support XML data sets, allowing your server applications to scale extremely well no matter which kind of client the user adopts.
Figure 2-10. In the near future, you might be able to send XML data sets to any kind of client.
So, as Figure 2-11 shows, you can improve scalability by moving state out of the business services. Just by using COM+ or MTS, permanent state naturally moves into the data services tier. As your application accepts a transaction, or reports it as failed, COM+ or MTS automatically deactivates your business objects for you. The resources they used are available immediately to other objects or for other uses. As your application moves temporary state to the client, your business objects can deactivate themselves, again allowing the resources they used to be reused by other objects or for other purposes.
Figure 2-11. Move state as far as possible away from business services and into user and data services tiers.