Modeling Loosely Coupled Use Cases
Last year, I got my first exposure to event-driven architecture (EDA), courtesy of several systems architects in our organization. Our systems architects operate in the “physical” (technology-specific) realm, whereas our systems analysts are active in the “logical” (technology-neutral) world. As a systems analyst, I became interested in how we could take advantage of EDA in our logical deliverables (why should architects have all the fun?). This article outlines a standard approach for representing basic EDA aspects in system use case models. A major inspiration for this approach is the wonderful article “How EDA extends SOA and why it is important” by Jack van Hoof. If you’re unfamiliar with this article, I encourage you to read it.
Use Case Coupling Patterns
As it turns out, there are two fundamental ways in which use cases can be coupled: command-and-control and publish-and-subscribe.
The command-and-control pattern covers scenarios where one use case initiates (not includes; see Postscript below) another use case (and that use case may initiate another use case, and so on). Any initiated use case gets executed as part of the initiating use case. They execute synchronously. An initiating use case knows an initiated use case by name (the reverse is not true). They are tightly coupled. They form a hierarchy of “superior” and “subordinate” use cases. An initiating use case controls the activation of initiated use cases (an initiating use case “is the boss of” its initiated use cases). This pattern is about functional request-and-reply processes.
The publish-and-subscribe pattern covers scenarios where (i) a “publisher” use case publishes, as an event object, an event of a certain type that took place during the use case’s execution, and (ii) one or more “subscriber” use cases that subscribe to events of that type get initiated and passed the event object. Any subscriber use case gets initiated and executed separately from the publisher use case. They execute asynchronously. No publisher use case knows any subscribing use case by name (the reverse is true as well). They are loosely coupled. They form a network of “upstream” and “downstream” use cases. No publisher use case controls the activation of subscriber use cases (no publisher use case “is the boss of” any subscriber use case). This pattern is about workflow processes.
Key questions are, of course:
- What facility accepts the event object from a publisher use case and passes it to the subscriber use case(s)?
- How does this facility know to which subscriber use case(s) it must pass a given event object and when?
To address these questions, we introduced the Event Manager as a logical system (i.e., represented by its own use case model). The Event Manager is separate from all other logically represented systems (i.e., that all have their own use case models) but is instrumental in connecting (use cases of) these systems into a network. (I use the generic term “system” to denote either a traditional system or, in a service-oriented context, a component; either classification works for the Event Manager.) This is summarized visually in Figure 1.
Figure 1: Consolidated use case diagram with Event Manager
Event Type Registry
The Event Manager is the keeper of the Event Type Registry. This registry contains:
- The modeled event types. These are the event types the Event Manager recognizes.
- Attributes for all event types. These are the common attributes that apply to all event types, such as event type, date/time published, publisher use case identifier (see also point 4 below). At minimum, each event object provides attribute values for those attributes.
- Attributes by event type. Each modeled event type may have its own mandatory and/or optional attributes that represent the data to be published about an individual event of that type. Each event object of that type provides attribute values for those attributes.
- Publishers by event type. For each modeled event type, one or more use cases that are modeled to publish events of that type. Each publisher use case is identified by the name of the system to which it applies (its “subject” in UML terms) plus its use case name.
- Subscribers by event type. For each modeled event type, one or more use cases that are modeled to subscribe to events of that type. Subscriber use cases are identified in the same way as publisher use cases.
- Subscriber notification requirements by event type. For each event type/subscriber combination, an indication of when the subscriber use case must be notified following the publication of an event of that type (e.g., immediately, overnight). These are part of the subscriber use case’s service-level requirements.
This registry is the central location where systems analysts record the modeling decisions they make when they apply the publish-and-subscribe pattern to model loosely coupled use cases to represent asynchronous system processes.
Event Manager Use Cases
Since the Event Manager is a logical system, it’s instructive to represent it through its own use case model. Figure 2 shows the model’s use case diagram.
Figure 2: Event Manager’s use case diagram
The Event Manager’s two use cases can be summarized as follows.
- Publish an Event. This is a “service” use case (i.e., initiated by [a use case of] another system). The use case’s initiating actor is called Event Publisher and represents any use case modeled to publish events. The Publish an Event use case accepts an event object, validates it based on the Event Manager’s knowledge recorded in its Event Type Registry (e.g., it is of a recognized event type, its mandatory attribute values are provided) and queues it for the Event Manager’s second use case. If the event object is invalid, the event’s publication is denied. Each publisher use case follows a standard pattern outlined below.
- Notify Event Subscribers. This is a time-initiated use case (i.e., a “scheduled process” use case) that empties the Event Manager’s event objects queue and initiates the appropriate subscriber use cases. The use case’s supporting actor is called Event Subscriber and represents any use case modeled to subscribe to events. Each event object triggers the initiation of one or more subscriber use cases, where each subscriber use case gets passed[GF1] the event object. The Event Manager controls which subscriber use cases to initiate and when to initiate them based on the Subscriber notification requirements in its Event Type Registry. Each subscriber use case follows a standard pattern outlined below.
The Event Manager’s use case model has been defined only to the extent necessary to give an idea of its basic responsibilities in loosely coupling publisher and subscriber use cases. (For example, not represented is whether, how often and at what intervals the Event Manager must notify a given subscriber again for any given event objects if the initial notification failed.) Defining the Event Manager this way accounts for these basic responsibilities in the realm of an enterprise’s logical systems models. How these and additional responsibilities are physically implemented is a different matter entirely.
Publisher use cases
Each publisher use case is modeled according to a standard pattern:
- Supporting actor. Event Manager. (In a use case diagram, this is a visual clue that the use case publishes events.)
- Standard publication step(s). “Initiate Publish an Event of Event Manager for
object.” (This justifies why the Event Manager is shown as a supporting actor in the use case diagram.)
Subscriber Use Cases
Each subscriber use case is modeled according to a standard pattern:
- Initiating actor. Event Manager. (Since a subscriber use case is initiated by [a use case of] the Event Manager system, it is a “service” use case.)
- Triggering event. Uses a standard format, like “One of the following events:
.” It’s important to state this in each subscriber use case so it’s clear which event types the use case is modeled to handle. The need for this is most obvious if and when the subscriber use case must handle events of a new type: it is not enough to change only the Event Type Registry by linking the subscriber use case to the new event type; the subscriber use case must also be updated to show how it’ll deal with events of that new type.
- Pre-conditions. These are conditions that must be true before the use case is allowed to be initiated. A subscriber use case’s standard pre-conditions include: “The event object contains values for all mandatory event object attributes,” and “The subscriber use case has not processed the event object successfully before.” Since no use case validates its own pre-conditions, it follows that the Event Manager must have done so. Indeed, validating the first pre-condition is part of the Event Manger’s Publish an Event use case (see above), while ensuring the second pre-condition is understood to be part of the Event Manager’s Notify Event Subscribers use case (see above).
- Validate the event object. Ensure that (i) the event object’s type is one of the types the subscriber use case is modeled to recognize, and (ii) all attribute values provided conform to the value ranges the subscriber use case is modeled to handle. This first step in the use case’s main flow amounts to ensuring that the publisher use case honoured the contract on which the subscriber use case is based. If not, the event object is rejected. (This is the “assertive” portion of a subscriber use case.)
- Decide whether to take action on the event object or ignore it. Just because a subscriber use case subscribes to events of a certain type doesn’t mean that it must take action for each and every event of that type. A subscriber use case may very well be modeled to ignore certain event objects. Ignoring an event object is modeled as an alternative flow, even though the subscriber use case’s goal is not achieved. The only alternative would be to model it as an exception flow, but that would be inappropriate since taking no action does not equate to a failure. (Typically, an alternative flow continues the pursuit of the use case’s goal, while an exception flow abandons that pursuit.) This use of an alternative flow seems to be unique to subscriber use cases.
The above means that systems analysts now have two distinct patterns available to them to model logically how system processes (system use cases) are related: tightly coupled or loosely coupled. Tight coupling amounts to functional decomposition of a “superior” use case into one or more “subordinate” use cases, each of which can decompose itself into one or more “subordinate” use cases, and so on. This pattern creates a use case hierarchy (see also Postscript below). In contrast, loose coupling is essentially about workflow where “upstream” publisher use cases indirectly trigger “downstream” subscriber use cases, each of which can indirectly trigger additional “downstream” subscriber use cases, and so on. This pattern creates a use case network.
How systems architects decide to implement physically the system processes that are represented logically as loosely coupled use cases is for them to decide. Systems analysts are able to indicate clearly and uniformly when and which system processes are loosely coupled. Systems architects can take their cue from that. Based on the technological means available to them, they can utilize the latest EDA technology or devise a more traditional implementation, like a batch process that periodically monitors a database for specific types of changes that informally reflect certain events. Whatever the choice, it doesn’t alter the logical representation. In that sense the logical and physical realms are loosely coupled themselves.
Contrary to what some may think, in the above modeling pattern the included relationship plays no role in connecting use cases. An association between a use case and its initiating actor represents that “at execution time” an instance of the actor and an instance of the use case will be connected via a link (an instance of the association). On the other hand, an include relationship between a base use case and an inclusion use case represents that “at execution time” the specification of the base use case and the specification of the inclusion use case will be combined into a single specification that will be used by an instance of the base use case (there will never be an instance of the inclusion use case).
This pattern doesn’t yet accommodate human event subscribers outright, but this may be added later. It must be noted that such an enhancement:
- Must not represent interrupts of long-running processes requiring a human decision. Those are examples of the command-and-control pattern, because a process passes control from an automated activity to a human activity and waits for the outcome of the latter before continuing.
- Must represent “downstream” human activities that are responses to normal conditions and exception conditions encountered during a process. These are examples of the publish-and-subscribe pattern, because the process publishes an event of a condition that it is not designed to handle itself and either continues or aborts; however, it does not pass control to a human being and consequently does not wait for the outcome of a human activity.
Extending that further, this approach can conceptually be applied to business use cases as well.
Don’t forget to leave comments below.