In the previous section, we looked at the intrinsic constraints that protect the structure and integrity of the database. You can do little to assist the user with data that violates an intrinsic constraint. Data must, at least eventually, be made to conform to the requirements of the relational model. Data that violates a business constraint, however, is a very different matter.
As I've said, you derive business constraints from the problem space rather than from the relational model itself. Remember that a data model is just thata simplified model of some part of the real world. As a system designer, you'll do your best to capture all the relevant aspects of the problem space in your model, but despite your best efforts you won't always succeed. Even if your model is perfect in every respect when it's first implemented, business conditions change. To be successful, your system must be able to handle the unexpected gracefully.
There are two reasons users might try to enter data the system isn't prepared to handle: either they entered the data by accident, or reality doesn't match the system model. (Actually, there are three reasons if you count intentional sabotage, but the pessimism of the average system analyst notwithstanding, sabotage is extremely rare, and, in any event, business rules are not the best way to handle it.)
If users enter the unexpected data accidentally, your only concern is to make it as easy as possible for them to correct the problem. At the very least, this means you should explain exactly what the problem is and what needs to be done about it. Where you can, you should also provide reasonable alternatives that can be implemented directly by the system. The dialog box shown in Figure 19-4, for example, might be triggered when a user enters the month and day of a date field backwards. The dialog box makes a reasonable guess about what the user meant to enter and allows the user to select the revised date with a single click.
Figure 19-4. This Dialog Box Explains an Entry Error without Issuing Ultimatums
When users enter the data accidentally, it might be a genuine accident, or it might be that they don't really understand what was expected. The dialog box shown in Figure 19-4 goes some way toward explaining the purpose of the DeliveryDate field, but it also includes a Help button to allow the user to find more detailed information. We'll talk about user assistance in detail in Chapter 21.
Reality vs. the System Model
The second reason users might enter unexpected information is because reality doesn't match the system model. For example, users might be trying to correct a situation in which an order has already been delivered but for some reason wasn't entered into the system. The delivery date for this order will be earlier than the order date, which could break a business rule. If you don't provide users with some guidance, they'll enter any meaningless delivery date they can get the system to accept, and the integrity of your database will be compromised. So it behooves you to be as creative as possible in thinking of as many exceptions as you can and help the user resolve them consistently. Figure 19-5 shows a possible response to a user entering a DeliveryDate earlier than the OrderDate. This example assumes that the OrderDate is set to the current system date by default and that the user ordinarily can't edit the field.
Figure 19-5. This Dialog Box Allows the User to Backdate an Order
Not all business rules can (or should) simply be overridden. Some business rules can't be broken, because the situation is impossible or because of statutory regulations, for example. But sometimes the question is not so much "whether" a rule can be broken as "by whom?" In Figure 19-6, for example, a user has tried to enter information about a sixth employee reporting directly to a manager, in violation of the rule that no manager can have more than five employees.
Figure 19-6. This Dialog Box Indicates that Only Someone with Appropriate Authority Can Override the Business Rule
The current user doesn't have the authority to override the rule. But the system makes available a secondary dialog box that allows a password and authorization code to be entered by someone who does have the authoritypresumably a supervisor or manager. Because individuals in authority aren't always available on demand, the first dialog box also allows the user to save the current record and provides instructions for entering the authorization code at a later date.
Providing the ability to override business rules can add significantly to the usability of the system, but it does so at the cost of adding complexity to the data model. In this example, you must add a field to the Employees table to contain the authorization code and include in the integrity constraint a reference to the authorization code. You must also provide a mechanism for putting records in abeyance if the authorization is delayed. One approach is to add a single Boolean field to the Employees table to indicate that the record is not (yet) valid. This flag can be used to find the invalid records later, when the user is ready to resolve the outstanding problems, or to filter the records when producing reports. Sometimes it's useful to know why the record is invalid, and in this case you can use a code field"MA" for "awaiting Manager approval" or "CC" for "Credit Check Outstanding," for example. This second approach provides more flexibility in handling the records.
The changes required to hold records in abeyance might seem straightforward, and for the most part they are, but they can propagate through the system in unexpected ways. Should the records held in abeyance be included in reports, for example? An abeyance code field can be useful when deciding how to handle these reports, since it allows you to determine which records should be excluded from a particular report or query.
If records in abeyance are included in reports and queries, you must also consider whether they should be marked as unreliable in some way, perhaps by grouping them together under a separate heading.
If you've determined that records held in abeyance, or a particular type of record held in abeyance, are not to be included in a report, you must then decide whether any associated records should be excluded. For example, if the problem is a record for an as-yet-unauthorized sixth employee, should only that employee record be excluded or all the records for employees reporting to the same manager?
All these issues must be considered before you decide to include the ability to override a business rule. The ability to do so increases the usability of the system, but the added complexity might not be justified. In simple systems it might be better to reject the record that doesn't comply with a business rule and handle the exception outside the computer system. If you decide to handle exceptional cases outside the system, be sure to say this in a data validation dialog box. Don't leave the user guessing how to proceed.