Use Case Preconditions: A Best-Kept Secret?
Introduction
Alistair Cockburn opened my eyes to the essence, elegance and effectiveness of use case preconditions. In [1], he discusses preconditions in just a dozen paragraphs, but these contain two statements that revolutionized my understanding of preconditions and their counterpart, postconditions.
This article starts with those statements, illustrates what I believe they imply, outlines key fundamentals, discusses how preconditions and postconditions reflect sequential use case dependencies and promote loose coupling between use cases, and ends with a reflection on enforcing preconditions.
Based on personal experience, as well as a cursory check of the Internet, use case preconditions still seem to be a best-kept secret. This article is intended to help change that.
Bless you, Alistair
Odd as it seems in retrospect, until I read Alistair Cockburn’s inspiring book [1], use case preconditions were in my blind spot. That changed with two statements at the start of the book’s chapter 6:
- “[A use case’s preconditions indicate] what the system will ensure is true before letting the use case start.”*
- “Generally, a precondition indicates that some other use case has already run to set it up.”
After reading these two statements, the clouds parted, an angelic chorus hummed a mystical chord, and a bright eco-friendly light bulb appeared above my head; I had discovered a nugget in Alistair’s gold mine of a book.
* A use case’s postconditions indicate what will be true after the use case finishes.
A picture tells the story
The next diagram illustrates the use of preconditions, using a simplified example.
This diagram reflects the preceding two statements in that:
- A user can only initiate a use case when the system has concluded that the use case’s preconditions are true.
- A use case’s preconditions result from executing one or more other use cases.
If we imagine a corresponding user interface, the function button that corresponds to a particular use case is enabled (a user can click on it) when the use case’s preconditions are all true, and disabled (grayed out) when at least one of the preconditions is false.
Another way to view this is as part of a “no false hope” strategy. A user is not given the impression that he can complete a certain function use case he can’t (i.e., if the conditions had been enforced as part of the use case instead of as preconditions, and at least one turned out to be false).
Some fundamentals
This section is based on [1] and on personal experience.
A use case’s preconditions can only exist within the system
Since a use case’s preconditions are enforced by “the system,” they can only exist within that system, which wouldn’t be able to detect them otherwise. A use case precondition cannot refer to a condition in the physical world that isn’t represented within the system.
The reflection at the end of this article shows that when it comes to enforcing preconditions, “the system” in its broadest sense refers to “the systems environment.”
A use case’s preconditions are the same for all its scenarios
Since a use case’s preconditions are true before the use case starts, it follows that they apply to whatever scenario* unfolds during an execution of the use case. This is one reason for not bundling multiple user functions into one use case (e.g., bundling Create, Read, Update and Delete functions into a Maintain use case), for they typically have different preconditions (and postconditions).**
* A scenario is a possible path through a use case.
** Moreover, it’s impossible to show in a use case diagram differences in user authorization; if one role (actor) is authorized to initiate one set of the bundled functions and another role (actor) is authorized to initiate a different set.
A use case doesn’t check its preconditions
Since a use case’s preconditions are “enforced by the system and known to be true” by the time the system allows the use case to start, they are “not checked again during the use case’s execution.” This is why:
- Preconditions are also called assumptions (a use case assumes they’re true).
- There is no need to write use case steps that check preconditions (doing so would miss the point of using preconditions).
Being mindful of these fundamentals yields clear preconditions and use case flows.
Of use case dependencies
Sequential dependencies
A use case precondition reflects a sequential dependency between use cases.
- Use case B with precondition C can only start after use case A has produced C as a postcondition.
- Use case B is executed after use case A; their connection is asynchronous.
Functional dependencies
In contrast, an include relationship (and a directed association as well) reflects a functional dependency between use cases.
- When use case A has an include relationship (or a directed association) to use case B, it means that the functionality of use case B is part of the overall functionality of use case A.
- Use case B is executed as part of use case A; their connection is synchronous.
Impact on use case models
This difference means it’s incorrect to use a functional relationship to represent a sequential dependency between two use cases—instead, use postconditions and preconditions. This is summarized in the diagram below for include relationships, using a simplified example.
Conditions enable loosely coupled use cases
What is loose coupling again?
Two use cases are loosely coupled when:
- The execution of the first enables the execution of the second.
- Neither has any knowledge of the other.
Kinds of loose coupling
One kind of loose coupling—between publisher and subscriber use cases connected via event objects and an event manager — is outlined in [2].
A different kind of loose coupling—between upstream and downstream use cases connected via conditions and user actions — is addressed in this section.
Point 1: Function use cases enable function use cases
In the above diagrams, postconditions produced by the execution of one or more upstream use cases enable the execution of one or more downstream use cases.
- As soon as a downstream use case’s preconditions are all true (i.e., they reflect postconditions produced by the execution of one or more upstream use cases), a user can initiate the downstream use case.
Point 2: Function use cases don’t know each other
In the above diagrams, upstream and downstream use cases don’t know each other.
- An upstream use case doesn’t know which downstream use cases a user can initiate next. It doesn’t pass control to any of them. It only produces certain postconditions.
- A downstream use case doesn’t know which upstream use cases a user initiated previously. It doesn’t accept control from any of them. If initiated by the user, it trusts that its preconditions are true, no matter which use cases produced them.
Actions outside function use cases
As noted earlier, in a use case model the system is understood to take actions outside use cases, for example by ensuring that a use case’s preconditions are true before allowing the use case to start. Similarly, a user can take actions outside function use cases, for example by selecting an object from a list before initiating another use case.
As setup for an example of the latter, assume that:
- The personal banking website from the first diagram also has function use cases View Accounts Summary, View Account Transactions, View Account Details and Change Account Details.
- The postcondition of the customer-centric View Accounts Summary use case is “The user is presented with a summary of the customer’s accounts.”
- A precondition of the three account-centric use cases is “The account has been identified.”
An implied user action is the selection of an account from the list of accounts presented by the View Accounts Summary use case. This selection, not the use case, creates the precondition for the three account-centric use cases.
This user action is implied because it is not part of any of the use cases.
- Adding it as the final step of the View Accounts Summary use case would make the use case overshoot the user’s goal, which is to view the accounts summary.
- Adding it as the first step of the account-centric use cases would make using the above precondition impossible and thus undermine the loose coupling between these use cases and the customer-centric one.
The implied system and user actions must be accommodated in the user interface derived from the use case model.
Tip: Don’t state who (an actor) or what (a use case) produced a precondition.
- Stating an actor introduces irrelevant, distracting and potentially misleading assumptions (what if the use case model is enhanced with a new actor who can also produce the precondition?)
- Stating a use case violates the principle that loosely coupled use cases don’t know about each other (and what if the use case model is enhanced with a new use case that can also produce the precondition?).
The condition is what matters, not how it came to be.
Benefits
Loose coupling between use cases in general:
- Inherently leads to use case models that are easy to create, understand and evolve.
Loose coupling between function use cases in particular:
- Enables analysts to produce use case models without built-in assumptions about the corresponding user interface (e.g., the sequence in which functions get presented).
- Gives user interface designers the greatest freedom in designing that user interface (e.g., a given function may be launched from multiple screens that each in its own way ensures that the preconditions of the corresponding function use case are met).
- Allows user interface designers to restructure an existing user interface without the need to change the corresponding use case model (e.g., moving function buttons around but adding no new functions).
Using conditions and user actions to model loosely coupled function use cases results in a highly modularized use case model whose function use cases are:
- Context-aware (aware of their preconditions, but not of other use cases).
- Single-purpose (because defining one set of preconditions for multiple purposes usually doesn’t work).
- Lean (because they are single-purpose and their preconditions are assumed).
When a system is modeled like this, it truly reflects the notion that the use cases represent the system’s operations, as mentioned in [3], because each use case represents a distinct capability the system provides to its users.
Preconditions for other use case types
The preceding deals with function use cases, but preconditions and postconditions can be used for the following use case types as well.
- Batch process (<
>) use case. As for a function use case, the preconditions reflect the use case’s sequential dependency on one or more other use cases. - Subscriber service (<
>) use case. As outlined in [2], use cases of this type are inherently modeled to be initiated by the event manager, and so they assume the event manager as their context. This means they have a standard set of preconditions (see [2]).
What enforces use case preconditions?
It’s one thing to state that the system ensures a use case’s preconditions are true before allowing the use case to start, but in a logical use case model it is typically implied that the controller is part of the system.
For a comprehensive understanding of controllers, we have to consider the physical implementation realm as well. The following table summarizes typical controllers and, relating the physical to the logical, the types of use case they control.
This summary shows that the enforcer of a use case’s preconditions isn’t necessarily part of the system to which the use case applies, so in the broadest sense “the system” means the systems environment when it comes to enforcing preconditions.
In conclusion
If you’re new to use case preconditions, I hope this article has whetted your appetite for them. If you’ve used preconditions before, I trust this article reconfirmed their usefulness and maybe even added a new angle. Preconditions certainly aren’t the only gold nugget in [1], and perhaps there’ll be opportunity to reflect on others.
Don’t forget to leave your comments below.
References
[1] Alistair Cockburn, Writing Effective Use Cases, 12th Printing, November 2004.
[2] Willem Van Galen, Modeling Loosely Coupled Use Cases, 30 April 2012, http://www.batimes.com/articles/modeling-loosely-coupled-use-cases.html.
[3] Willem Van Galen, The System: Don’t Model A Use Case Without It!, 11 September 2012, http://www.batimes.com/articles/the-system-dont-model-a-use-case-without-it.html.