Now let's move a little closer to reality. We need to find out more about what the system will have to do for us, and about which classes should do the job at hand. We already have our framework; we even know some of the classes needed. Let's go to work on it.
Our first job is to create a small diagram that illustrates at least some of the classes that will be involved in our little sample application. We need at least a graphical user interface (GUI) object; we'll use for the time being a Visual Basic form. We also need a facade class, which will be the only place the form needs to go to for business services. According to our design pattern, we also need recordsets to keep our data and to move it between objects. Figure 5-2 shows the relationship of these objects to one another.
Figure 5-2. The frmHorseMaint form object relies entirely on the fcdHorseMaint facade object for business services. Both of them use an ADO recordset for state keeping and data transformation.
A use case is a high-level statement of something an actor needs to do with the system. Every use case specification needs to list things a user needs to do to carry out the use case. One of the use cases our application needs to support concerns maintaining information on horses trained to run in horse races. Let's list a few of the things a user needs to do in that use case. Then we can see the kind of services required for each of these things. Here's such a list:
Let's see how each of these requirements could be designed and implemented in a first prototype of the application. In the following several chapters, we'll refine that prototype until it becomes the real application.
Let's begin to explore how we might accomplish this. Figure 5-3 shows a first version of our Visual Basic form in its proper environment—in Visual Basic's integrated development environment (IDE).2
Figure 5-3. A text box and an OLE DB DataList control will help the user find the right horse.
The Search Criteria text box allows the user to type a name pattern for the horses that should populate the OLE DB DataList control beneath it. This name pattern must travel all the way to the database to be used as a selection criterion. We need a mechanism for that, and also one for displaying the horse names coming from the database. The diagram in Figure 5-4 shows the methods that form such a mechanism.
Figure 5-4. This is an enhancement of the diagram in Figure 5-2. It reflects the mechanisms needed to fetch and display a list of horses.
Notice a few interesting details in Figure 5-4:
A UML association translates to a Visual Basic module variable. This means in principle that the life cycle of the recordset will be the same as that of the form. A UML dependency, in contrast, doesn't translate to anything at all. It exists to indicate a dependency, such as the fact that one object has to call another one for service.
Figure 5-5. In this diagram, a dependency relationship has been given a name corresponding to the method that needs to be called.
Just a few words about the facade object. The facade object itself won't go to the database for the information requested. Instead, it delegates that work to an object of the Horses class, which in turn delegates it to a HorseFetcher object. Right now we don't care. All we want to do at this particular moment is define exactly which services the user application needs from the server application, how it should request these services, and how it should get access to the results of the services. We've already defined these aims for the very first action from the user, which is to ask for a specific list of horse names. The facade object exposes this service with the GetHorseList method.
Before doing anything else, we should specify exactly what this method does. Since we don't know at this point how it will perform its service, we'll only be able to describe what the service is, what it will be called, and what it will return. Figure 5-6 gives an example of such a specification.
Figure 5-6. The GetHorseList method specification. Later on we'll enhance it to specify which services the method will use to do its job.
Later on, when we use the Rose model to generate Visual Basic code, the code generator will include this specification as a method comment.
Now let's see how the facade class exposes the next mechanism that we need to use.
This is a tricky task, much trickier than it would seem to be on the surface. If the goal were to create an ordinary two-tier application, the recordset already fetched would probably contain the information needed to display all the data about the selected horse. Furthermore, the recordset would still be in touch with the database, so every change to a record would go directly to the database as soon as the user made another record the current one.
Not so in this case. Here we're talking about a three-tier application, and the recordset isn't connected to the database at this point. Furthermore, the recordset is small in that it contains only the names and IDs of the horses selected. The recordset contains names because they must appear in the data list control, and it contains IDs because we need them to get full information about a selected horse.
So we need to go to the database again, this time to select data on a single horse, identified by its ID. Figure 5-7 shows how we've enlarged the model to accommodate this requirement.
Figure 5-7. The model now reflects the capability to get and display a selected horse from the database.
The changes in the diagram speak more or less for themselves, but here are a few comments just the same:
The first of these associations, the one named rsHorses, contains names and IDs of all the horses displayed in the list. The second one, named rsHorse, contains full data on the horse selected and helps display that data in the main part of the form (as in Figure 5-9).
Figure 5-8. The GetHorseById method specification.
The form in Visual Basic's IDE now needs a number of fields so that it can display detail data for a single horse. Let's add some simple text boxes to our form, as in Figure 5-9. Later we can enhance the form by exchanging a couple of the text boxes with other controls better suited to our purpose.
Figure 5-9. The form now contains fields to display data that describes a single horse.
Next on our list of user requirements is the need to change the content of some of the fields that display data for the horse. Because we're in the process of designing a facade class, we don't need to even think about how users should interact with the form. That's an issue for client developers. In the test form we're designing now, we'll use data binding between the rsHorse recordset and the form fields. In the next chapter, you'll see how this works out.
In any case, you must make sure that the user can send modified information to the database. In the form, a simple Save button is enough, and you can see it in Figure 5-10.
Figure 5-10. The form now has a Save button.
Furthermore, we'll need a save method both in the form and in the facade class. In the form, such a method will be simple enough. The method will know by itself which recordset contains the changes. It will also know how to delegate the job to the facade object, which in turn will delegate it to other objects in order to move the new information right into the database.
The facade method must be treated differently. It needs to be told about the recordset, and also about the content of the recordset, when called. Take a look at the new SaveRecord and SaveHorse methods in Figure 5-11. Notice that the SaveHorse method takes a recordset as its only argument.
Figure 5-11. The model can now save a Horse record to the database.
In Figure 5-12, you can see the specification for the SaveHorse method in the facade class.
Figure 5-12. The SaveHorse method specification.
Looking back at the class diagram in Figure 5-11, you might ask yourself why the SaveHorse method doesn't return a status code. After all, such a return code would tell the form object whether or not the save operation was successful. Shouldn't the form object be told?
Yes, it should, and it will; at least if there is an error. As we see it, you can choose from two ways to handle errors:
You should use only one of these two ways of handling errors and stick with it. Don't fall into the trap of using one of them sometimes, the other one on other occasions. Pick one only!
Our pick is the second one—the Basic way. We'll use it for the rest of this book, and we recommend it to you as well.
In a way, deleting a horse follows the same pattern as saving modifications. When you design a method for deleting horses, you must consider rule 6, which we introduced in Chapter 3 and whose implementation we described in Chapter 4: horses that have participated in races must not be deleted but merely deactivated.
This rule, however, doesn't affect our work at this point. We still need a way for the user to order the deletion. In the facade class, we also need a mechanism for sending that command to the inner parts of the server application. In Figure 5-13, you can see how we use the horse's ID and the DeleteHorse method to delete the horse's record rather than send the recordset to the facade.
Figure 5-13. Delete operations are now included in the model.
You could, if you prefer, include the recordset with the delete message instead of including the ID.
You could even use the Save method to delete a horse from the database. If you mark the record for deletion and then save it, a SQL DELETE statement will be sent to the database server. Each record has a state property, informing ADO of the type of modification—if any—that the record has been subjected to.
It's all a matter of taste, really, which of these three means to use. Whichever one you choose, however, you must take into account the fact that a requested deletion might be changed to an update, deactivating the horse. When this happens, the user must be told about it. Assuming we've chosen the first of the three means (seen in the diagram in Figure 5-13), let's see how we make sure the user is informed.
In the documentation of the facade class, we inform anyone who might read the code about the existence of this rule and, most important, about how the method should react when a deletion is changed to a deactivation. You can see this in Figure 5-14.
Figure 5-14. The DeleteHorse method specification.
We don't document the specifics of the rule in the facade class. This is because it's not the responsibility of the facade object to check for the rule. It is, however, important for the creator of the facade class to know that there is such a rule and that facade objects of the class might be affected by it. This is what we try to describe in the facade class documentation.
When a user wants to add a horse to the database, he or she needs an empty record in the rsHorse recordset. Right now we don't have to consider how to give the user such a record, but we must specify the interface to the mechanisms that we need. This is easy enough, as you can see in Figure 5-15.
Figure 5-15. The DisplayEmptyRecord and GetEmptyRecordset methods for setting up empty recordsets are added to the diagram.
You can see that two new methods have been added:
Let's just take a look at the specification of the facade object's GetEmptyRecordset method. See Figure 5-16.
Figure 5-16. The GetEmptyRecordset method specification.
All the methods we've introduced in this chapter will be implemented in several classes spread over all five tiers of our basic architecture. The next eight chapters are dedicated to the design and implementation of these classes. However, in Chapter 6, "Reducing Time to Market," we describe a way to reduce time to market by starting a number of activities at this early stage rather than much later in the project.