Use Case Patterns
Use cases must meet two overlapping yet contrary goals, namely (1) specify functional requirements as expressed by business analysts, and (2) provide a clear, consistent, and non ambiguous basis to system analysts.
Whereas mutual understanding is certainly helpful, the risk is twofold:
- Business analysts may unknowingly introduce biases into system architecture.
- System analysts may reinterpret business needs to fit them into their modeling languages.
The objective of use case patterns is to bridge the gap between business and system models without altering requirements contents nor preempting architectural options. For that purpose, representation patterns are to map execution units associated with use cases into functional components along the functional architecture perspective.
Architecture based Use Cases
As often noted, the fundamental conundrum with use cases is to describe system interactions with users without introducing architectural bias. One way to solve the problem is to tell apart the functional level, which duly belongs to use cases, and the technical one, which doesn’t.
Let’s first consider pared down use cases, illustrated by support services.
- Since access to persistency units potentially affects all business processes, it is the most constraining functionality, and therefore the first to be set apart. Corresponding services are usually natively supported by persistency systems.
- The architectural footprint of execution units is by nature more limited and is usually the backbone of use cases. Checking access rights and authorizations is a standard example.
- Managing access to entry points should be set apart since they are executed locally, before calling on system shared resources. Moreover, since roles are defined within organizations, representing them in interactions is useless if they are processed locally. For instance, identification can be achieved by matching some input with a value object stored or generated locally, or even obtained through another service.
Distribution & Synchronization
The next step is to take into account distribution and synchronization constraints.
- Distributed use cases are executed in more than one location, i.e using resources managed independently and/or run under different clocks. It must be noted that those constraints originate as business requirements, e.g geography, independently of system platforms. Distributed entry points involve matching inputs/outputs from different locations before any processing.
- Synchronized use cases, distributed or not, must take into account functional dependencies between persistency or execution units identified independently. Synchronized entry points involve simultaneous inputs/outputs managed locally.
Use Cases & Architecture Tiers
Use cases can be organized along the linking principles set for activities: whereas shared functionalities, mandatory or optional, are represented by separate use cases, local ones should only be described at activity level. Keeping that principle in mind, <<include>> connectors describe a delegation relationship, not to be confused with the semantics of [structure] composition.
That provides the first archetypes for cross tiers use cases: batch, transactional, and real time.
- Batch use cases can be triggered independently of contexts
- Transactional use cases are triggered by roles played by nondescript agents. In other words system coupling (aka synchronization) is symbolic, as defined by dialog states.
- Real time use cases are triggered by roles played by physical devices. In other words system coupling (aka synchronization) is actual, as defined by physical states.
Pattern Criteria & Use Case Profiles
Architecture based patterns take into account use cases footprint across architecture tiers. That can be expressed by profiles depending upon binding constraints:
- The lest constrained use cases are computations executed instantly and independently of contexts and persistent representations.
- Next come batch use cases, whose computations on persistent representations cannot be executed instantly yet don’t depend upon context events or agent expectations.
- Transactions do depend on agent expectations; they also take time to complete.
- Collaborations must take into account multiple agent expectations through messages. As a corollary they are bound to operational contexts since the different roles are not necessarily managed by a single system.
- Control use cases must deal instantly with events and the status of actual devices.
Setting those criteria upfront should greatly help to enhance use cases modularity and limit their coupling, and for that purpose use cases may be stereotyped along two kinds of dependencies: flow dependencies are logical and set by the targeted objects and nature of operations; execution dependencies combine flow dependencies and operational constraints..
Functional (flow) dependencies are stereotyped depending on targets, namely, :
- Symbolic description of actual objects processing.
- Processing of persistent symbolic representations.
- Processing of interactions.
- Synchronization of activities.
- Processing of transient symbolic representations.
- Control of actual processes.
The same classification is applied to execution dependencies as stereotyped with states depending on:
- Processing of actual objects.
- Processing of symbolic representations.
- Agent’s expectations (“state of mind”).
- Communication support and events synchronization.
- Execution of symbolic computation.
- Control of actual processes.
The first criterion to consider is the coupling with actual contexts. For that purpose, use cases should be designed along the architecture hierarchy: non shared activities include shared ones which include accesses to persistency units. When that’s not practicable, the direction of control should be explicit:
- Pull strategies give shared activity control over non shared ones, e.g a transactional activity may include polling of physical devices.
- Push strategies give control to agents, with possible constraints on synchronized execution, e.g a real time activity may include transactional or even batch activities.
One can stereotype use cases as functional (1 and 2) or execution units (3), and execution units as instant or state driven:
- Alarms must be processed without delay nor interruption. The use case cannot be performed on its own (its trigger is set by the monitoring use case), but defining it as abstract would be counter-intuitive.
- Computing targeted levels is a functional unit that cannot be performed on its own.
- Managing canals can be triggered by a manager or from a monitoring use case. Since the use case has no direct triggering event there is no ambiguity.
Use cases regroup alternative execution paths whose actual realization is set by extension points. Those extension points are to be realized by synchronization ones in activity and sequence diagrams, and may also be associated with partitions, themselves realized by power-types. One profiling step further could be to consider governing rules associated to extension points.
The first rules to be considered are those targeting actual context as can be known once use cases are running:
- Taxonomy of operational contexts, i.e nature of resources supporting use case execution.
- Event specifics, e.g value captured by sensor, invoice amount, etc.
- Agent authorizations, to be retrieved after he’s been identified.
Next one should consider taxonomies of symbolic representations, possibly in relation with event specifics and agent authorizations.
Finally, choices made along execution paths will be taken into account, possibly set in relation with those mentioned above, as well as with taxonomies of operations. At that stage one should distinguish between symbolic rules governing computations, and operational ones, governing the execution of actual processes.
Given the type of governing rules it’s possible to qualify use cases without a detailed specification of activities. That is arguably pivotal when assessing the size and complexity of use cases under consideration.
The objective is to factor out activities shared across applications. The difficulty is that activities do not necessarily coincide with use cases, as some cannot be neatly described as interactions between actors and systems.Two patterns should be introduced first to circumscribe the problem, one for abstract activities, the other for concrete functionalities.
Some activities may be performed within different contexts without lending themselves to actual execution. That’s obviously the case for operations (performed by objects), but that may also apply to sequences, either because they are incomplete or because their triggering conditions (event and actor) or outcome can only be defined from within another use case. In that case they can be associated with abstract use cases to be included in concrete ones.
With the opposite situation, the shared activities are functions that can be actually executed on their own. Yet, as already noted, use cases are not functions, and therefore cannot exchange data or control flows. In that case, the targeted activities can be associated with a service to be accessed through roles.
Ambiguities can be avoided by stereotyping use cases as functional or execution units, and actors as roles or agents:
- Invoicing and trucking are both concrete but the former can be executed on its own while the latter can only be performed within a rescuing use case.
- Independent execution units (eg invoice) must be associated to a triggering event whose source may remain implicit, i.e non associated with a specific role.
- Interactions should eventually be characterized by a data (for roles) and/or control (for events) flow.
- When specified, roles may be associated with triggering events simply contributing to use case realization.
- Moreover, specified roles may be performed anonymously or identified by the performing agent.
The Case for extends
Choosing between UML’s <<include>> and <<extend>> is not straightforward, which may point to some lack of sound rationale. Both apply to shared activities, which may or may not be performed independently: using the former is erring on the side of caution, so why take risks with the latter.
On the other hand it may be helpful to set apart alternative paths by defining extension points, especially when the same use case can be triggered by different actors. Another benefit would be to link use cases with partitions, which, as already explained, is a very robust way to define variants without introducing inheritance before its due time.
That provides the necessary and sufficient conditions as to when introducing <<extend>> connectors:
- Necessary condition: a specialized use case combining extended and extension should make sense, even if inheritance is not to actually used in models (shadow model).
- Sufficient condition: the associated extension point must be meaningful independently, usually through a reference to a partition.
The Case for Abstract
When different use cases include the same activities it may be tempting to factor them out as an abstract use case. Yet, use case semantics are not the same as class semantics, in other words abstract use cases don’t describe objects with features to be inherited. What will make them abstract is that, as specified, such interactions cannot be instantiated.
For instance, while managing overdrafts can stand as a self contained use case, moving money is a processing operation defined independently of interactions.
As a rule, abstraction should only be used for use cases triggered by actors or events whose type is to be settled during execution. That is best illustrated by by system connections and users access.
A concrete use case can be included for the connection of any user (a), subsequently qualified once identified and authorized. That scenario is not possible with an abstract use case for which there is no concrete actor (b).
When the target is not business operations but triggering conditions generalization can be used instead of inclusion (c).
Subsystems & Services
Use cases can seldom be considered in isolation, as if supported by standalone applications confined into single systems. They usually involve other use cases, often set across distributed systems. Wrapping the whole into the same model may prove to be impracticable as well as unwise:
- Impracticable because once pulled, the net will usually bring in a mixed bag of legacy and planned use cases, with a corresponding mix of responsibilities.
- Unwise because that cannot be achieved without abandoning the black box perspective and foraging deep into subsystems whose architecture may be still undefined.
From Patterns to Architecture
Representation patterns bring significant benefits both for business and system analysts:
- On the business side they foster pared down requirements focused on functional architecture.
- On the system side they provide a better understanding of business concerns together with their impact on system architecture.
In between, representation patterns provide with a way to check the consistency of use cases regarding functional architecture by checking stereotype compatibility of included use cases against including ones.