Designing the Main Business Classes

[Previous] [Next]

Before we go on to design the services of our main business classes, we should summarize what we've learned so far in this and earlier chapters. We've learned that actors have use cases, so it's a good policy to identify the actors in our system as early as we can. When we know the actors, we can figure out the use cases for each actor. And when we know the use cases, we're able to decide which facade classes we need to support the use cases. So a key activity in system development is to capture actors and use cases as early in the development process as possible. We shouldn't make any detailed decisions about anything until we have a clear picture of at least the most important use cases for the most important actors. When we have, we can describe each of these use cases in detail, using the language of the business rather than the language of technology. We want project sponsors and users to understand these descriptions because they have to sign off on them.

Our next tasks are to design one facade class for each of the most important use cases and to turn these facade classes into prototypes as we described in Chapter 6, "Reducing Time to Market." We also create test forms so that we can test our facades and find the most obvious shortcomings of the requirements or the design ourselves. Then we turn the facade test stubs over to GUI developers, allowing them to create real HTML pages or forms that work against our facade prototypes. This allows both the GUI developers and the real users-to-be to see whether we're on the right track and to give us, we hope, the early proof of concept we've been talking about and desperately want.

When most facades exist and are validated by GUI developers, by potential users, and also by us, we've established a platform that we can safely continue building on. It's time to design the main business classes and the main business interfaces.

Different facade classes demand different kinds of services from the main business classes. Your job is to make sure that your main business tier serves each of the facades in the facade tier well, which in many cases means that you must compromise. For example, rather than having a main business class that supports 10 facade classes with five methods doing almost the same thing and having almost the same set of arguments, you might consider making them into one method with a set of arguments that might not be the best one for any of the 10 facades but still would support them all. You should also make your main business objects expose all their methods through separate interface classes, later to become COM+ interfaces. Some of these interfaces, such as IMaintADO and IViewerADO, might be standardized and generalized, while others are specifically meant for a certain class. The IRaceViewerADO and IRaceEntrViewerADO interfaces in Figure 18-10 are examples of such interfaces, each being specific to one class only.

Figure 18-10 looks at things from the perspective of a specific facade class. For each main business class in that diagram, there's also at least one different perspective. Such a different perspective might look at things from the view of the main business class itself. Figure 18-11 is a diagram that looks at the world from the perspective of the HorseManager class, and it doesn't care at all about any of the facades the class must support.

click to view at full size.

Figure 18-11. The HorseManager class and the interfaces it has to implement.

Every class must implement every method it exposes through an interface. Preferably, it should use private methods to do that, as in Figure 18-12. The situation now, for the HorseManager class, is that we know a lot of the interfaces it must implement; therefore, we know quite a lot about the requirements for that class. Saying that, let's forget for a moment that we have only glanced at a few of the use cases interested in that class. In real life, you shouldn't forget; you should try to find out as much as possible about at least the most important use cases and their facade classes before you begin designing main business classes and their interfaces. But right now we can sin, and we can blame our sin on pedagogical considerations.

click to view at full size.

Figure 18-12. Partial implementation of the IViewerADO interface.

Figure 18-12 shows how the HorseManager class at least partially implements the IViewerADO interface. A couple of interesting things to notice are:

  • The HorseManager class implicitly "understands" that there might be a similar IViewerXML interface. This is because we developers are farsighted. Even if there's no need for an XML interface today, we understand that such a need will arise in time. So we've used specific private methods in the HorseManager class to implement the two methods exposed by the IViewerADO interface. For example, the GetById method exposed by IViewerADO is implemented in the class by the private GetByIdAsRS method. Later, when the class also implements an IViewerXML interface, it will probably implement the GetById method exposed by that interface in a private GetByIdAsXML method that returns a string rather than a recordset. Being farsighted doesn't help much today, but chances are it makes the future simpler and nicer.
  • The HorseManager class simplifies the list-fetching routine, making it filter a name pattern only. Later it might use similar private methods to implement other, less simple, filtering conditions. There's nothing wrong with simplifying the private methods of the class that implements the interface, just as long as the class supports all the current needs.

Figure 18-13 shows a later version of the same diagram. Here the IMaintADO interface has been added. Furthermore, the HorseManager class has received private methods to implement the three new methods of that interface (new in the sense that they're not present in the IViewerADO interface earlier implemented by the class).

click to view at full size.

Figure 18-13. Three additional methods implement the three extra methods of the IMaintADO interface. The first method specified in the interface (GetById) is a duplicate of the corresponding method in the IViewerADO interface.

Figure 18-14 is interesting in another way; it adds the IHistory interface. This interface has methods that return ADO recordsets, just as the two other interfaces in the diagram do, but in contrast with the other two, the name of the IHistory interface doesn't imply this.

click to view at full size.

Figure 18-14. The IHistory interface has several weak spots.

It's not a fault for an interface such as IHistory to have a name that doesn't imply the character of the interface, but since we've been so farsighted before it isn't good design either. The name of this interface also isn't consistent with the names of the other two. Changing it to IHistoryADO would improve the clarity of the design as a whole, especially later when we might have added an IHistoryXML interface to the design.

Anyway, we're much better off finding this out while modeling the application than finding it out after programming a number of classes that implement the IHistory interface. When we change the name of the IHistory interface in the diagram in Figure 18-14, the change is global. It comes through in every place in the entire model where the interface is used, not only in the diagram in which we change the name.

Still looking at Figure 18-14, consider the names and data types of the first argument of the methods in the IHistory interface. Both methods specify a lngHorseId long integer parameter. Obviously this is very wrong since this interface was meant to be general and reusable at least in the horse racing application but perhaps by other applications too. For example, it's not difficult at all to see the need of historical or statistical information about a customer or a product in an e-commerce application. Figure 18-15 shows a better design.

click to view at full size.

Figure 18-15. The design of the IHistoryADO interface is now improved. The HorseManager class also implements it, and in a more personalized fashion.

In Figure 18-15, we've improved the naming and the design of the interface now named IHistoryADO. If you look at the first argument of the methods exposed by the interface, you can see that they're now declared as Variants rather than as integers. A variable declared as a Variant is so much more flexible than one declared as an integer. It can hold, for example, not only an integer value or a string but also an array with several values that together form a key or an identifier.

We've also set up private methods in the class to implement this interface. Interestingly, the private GetHistory and GetStatistics methods of the HorseManager class use the data type of the object type it handles: lngHorseId As Long. This is probably good because you don't normally need to be general in specific cases. Also, Variants are more expensive in space as well as in performance than other data types, so you shouldn't use them when there's no advantage in it.

Designing the Data Access Classes

Now that you know quite a lot about the chores of the HorseManager class, it's time to design the data access classes it will delegate data access tasks to. According to our architecture, you almost always need two data access classes:

  • One for reading operations. We tend to call such classes fetchers. In MTS or COM+, we set their transactional attribute to Does Not Support Transactions.
  • One for modifying operations. We call these transactional servers, or modifiers. In MTS or COM+, we normally set their transactional attribute to Requires A Transaction. Some of them might use Supports Transactions; others might specify Requires A New Transaction; but the most commonly used attribute should probably be Requires A Transaction.

Designing data access classes is often fairly simple, which Figure 18-16 shows. As you can see in the diagram, the public methods of the two data access classes look exactly like the corresponding private methods in the main business class.

click to view at full size.

Figure 18-16. Data access components now added to support the HorseManager class.

Sometimes, however, you need internal methods that support the first set of methods that you identify for a data access class. In other words, the first step of the process can be mechanized, but you can't always settle for the result mechanical means give you. Another thing: data access classes are so closely related to their main business class that they're fairly easy to modify together with the main business class. Normally, the only client of a data access class is its main business class.

So you can take chances with your data access classes. For example, you can bet on the chance that data access objects always return recordsets, leaving it to the main business objects to convert them to XML data sets if the client wants. This should be a safe bet because even if it turns out to be a losing bet, no real harm is done. The entire mechanism is internal to your server project, and you're quite free to design it as you want, just as you're equally free to modify the design as well as the implementation whenever the need arises. The main and most often exclusive purpose of a data access class is to perform data access services for one and only one main business class. Figure 18-17 shows such a design, in which you've made the bet that returning and receiving ADO recordsets is a good thing for two data access classes.

click to view at full size.

Figure 18-17. Betting that data access objects might always return ADO recordsets, leaving to the main business objects any task of converting to XML or whatever.



Designing for scalability with Microsoft Windows DNA
Designing for Scalability with Microsoft Windows DNA (DV-MPS Designing)
ISBN: 0735609683
EAN: 2147483647
Year: 2000
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net