Skip to content
academia.sh

Lesson 04 / 10

Use Case Diagrams

Counting separately what the actor, scenario, and system-boundary notation carries and what it drops: seven internal steps omitted on purpose, two links dropped by accident, and 512 systems collapsing into the same diagram.

Contents

The previous topic measured the static side of a system: which class is linked to which, which direction a link runs, how many distinct component partitions the same class set collapses into. Every question asked there looked at the system in its state of not running.

This topic looks at the moment the system runs: who starts what, in what order messages travel, in what order work can be done, what states an object passes through. The first of the four behavioral notations carries the least information of the four — and that is not a flaw. The use case notation deliberately leaves the inside of the system empty. This lesson’s job is to count that emptiness, because until it is counted, there is no way to know whether a gap was left on purpose or fell through unnoticed.

The lesson’s question is not which diagram type fits which question; that measurement was made over a set of eighteen questions in the Architectural Decisions and Documentation course of the Software Architecture curriculum, and is not repeated here. The question here begins after a type has been chosen: how many distinct systems does the chosen type fail to tell apart.

The Four Fields the Notation Carries

The use case notation carries four kinds of information: the actor, the scenario, the link connecting an actor to a scenario, and the system boundary, which states what is the system’s own work and what belongs to a neighbor. That is the whole list. There is no order, no condition, no internal step, no data.

In the repair-shop example, these four fields correspond to the following. There are two actors: the customer and the intake clerk. There are three external facts: opening a work order, checking status, picking up. All three are started from outside the system, and all three are the workshop’s own work — that is what the boundary states, and these three ownership decisions are three separate facts. Inside the system, by contrast, seven internal steps run — allocating stock, assigning a technician, reserving a workbench, processing a line item, generating a document, computing cost, keeping records — and none of them is started by an actor.

Total: 20 facts — 2 actors, 3 scenarios, 5 actor–scenario links, 3 ownerships, 7 internal steps.

Building and Counting the Diagram

The model below does not draw a diagram; it builds the list of fields the diagram carries and finds, by exhaustive count, how many distinct systems produce that same list. The real set of links has five links (DD1); the drawn diagram writes three of them (DD2).

"""Use case notation: intentionally omitted facts and inadvertently dropped
facts are counted separately. The count is exhaustive, not an estimate."""
from itertools import product

INTERNAL_FACTS = ["allocating stock", "assigning a technician", "reserving a workbench",
                   "processing a line item", "generating a document", "computing cost",
                   "keeping records"]
EXTERNAL_FACTS = ["opening a work order", "checking status", "picking up"]

# DD1: the workshop's real actor-scenario links and each scenario's owner
ACTORS = ["Customer", "IntakeClerk"]
REAL_LINKS = [("Customer", "opening a work order"), ("Customer", "checking status"),
              ("Customer", "picking up"), ("IntakeClerk", "opening a work order"),
              ("IntakeClerk", "picking up")]
REAL_OWNER = {s: "system" for s in EXTERNAL_FACTS}   # all three are the workshop's own work
# DD2: the drawn diagram writes three of the five links
DRAWN = [("Customer", "opening a work order"), ("Customer", "checking status"),
         ("Customer", "picking up")]


def notation(links, internal, owner, drawn_links, drawn_internal=(), boundary=True):
    """The notation carries only this: actor, scenario, actor-scenario link,
    system boundary. An undrawn link or internal step never appears in the
    notation; if the boundary is not drawn, whether a scenario is the
    system's own work or a neighbor's is never stated."""
    return (tuple(ACTORS),
            tuple(EXTERNAL_FACTS) + tuple(sorted(i for i in internal if i in drawn_internal)),
            tuple(sorted(b for b in links if b in drawn_links)),
            tuple(owner[s] for s in EXTERNAL_FACTS) if boundary else None)


def symbol_count(drawn_links, drawn_internal=(), boundary=True):
    return (len(ACTORS) + len(EXTERNAL_FACTS) + len(drawn_links) + (1 if boundary else 0)
            + 2 * len(drawn_internal))


def systems_collapsed(drawn_links, drawn_internal=(), boundary=True):
    """How many distinct systems produce the same diagram: exhaustive count."""
    target = notation(REAL_LINKS, INTERNAL_FACTS, REAL_OWNER, drawn_links,
                       drawn_internal, boundary)
    count = 0
    for link_mask in product((0, 1), repeat=len(REAL_LINKS)):
        links = [b for b, m in zip(REAL_LINKS, link_mask) if m]
        for internal_mask in product((0, 1), repeat=len(INTERNAL_FACTS)):
            internal = [i for i, m in zip(INTERNAL_FACTS, internal_mask) if m]
            for owner_mask in product(("system", "neighbor"), repeat=len(EXTERNAL_FACTS)):
                owner = dict(zip(EXTERNAL_FACTS, owner_mask))
                if notation(links, internal, owner, drawn_links, drawn_internal, boundary) == target:
                    count += 1
    return count


total_facts = (len(ACTORS) + len(EXTERNAL_FACTS) + len(REAL_LINKS) + len(EXTERNAL_FACTS)
               + len(INTERNAL_FACTS))
print("SYSTEM  :", total_facts, "facts =", len(ACTORS), "actors +", len(EXTERNAL_FACTS),
      "scenarios +", len(REAL_LINKS), "links +", len(EXTERNAL_FACTS), "ownerships +",
      len(INTERNAL_FACTS), "internal steps")
print("NOTATION:", symbol_count(DRAWN), "symbols")
print("COST    :", systems_collapsed(DRAWN), "systems collapse into the same diagram")
print()
print("  intentionally omitted (notation cannot carry) :", len(INTERNAL_FACTS), "internal steps")
print("  inadvertently dropped (could have been carried):",
      len([b for b in REAL_LINKS if b not in DRAWN]), "links")
for b in REAL_LINKS:
    if b not in DRAWN:
        print("     not drawn:", b[0], "->", b[1])
print("  drawn diagram             : symbols", symbol_count(DRAWN),
      "| collapses to", systems_collapsed(DRAWN))
print("  all five links drawn      : symbols", symbol_count(REAL_LINKS),
      "| collapses to", systems_collapsed(REAL_LINKS), "<- BASELINE of the notation")
print()
print("what happens to the baseline when a field is dropped")
print("field set                    symbols  collapse  bits carried")
BASELINE = systems_collapsed(REAL_LINKS)
for name, links, boundary in (("actor+scenario+link+boundary", REAL_LINKS, True),
                               ("link dropped", [], True),
                               ("boundary dropped", REAL_LINKS, False),
                               ("both dropped", [], False)):
    d = systems_collapsed(links, (), boundary)
    print(f"{name:29s} {symbol_count(links, (), boundary):7d} {d:9d} {(d // BASELINE).bit_length() - 1:14d}")
print()
# DD3: second configuration. Two internal steps are drawn as an "included use case."
EXTRA = ("generating a document", "computing cost")
print("if two internal steps are drawn as an included use case:")
print("  symbols", symbol_count(REAL_LINKS), "->", symbol_count(REAL_LINKS, EXTRA),
      "| collapse", BASELINE, "->", systems_collapsed(REAL_LINKS, EXTRA))
print("  cost: number of actorless scenarios 0 ->", len(EXTRA),
      "| external-fact ratio", f"{len(EXTERNAL_FACTS)}/{len(EXTERNAL_FACTS)}",
      "->", f"{len(EXTERNAL_FACTS)}/{len(EXTERNAL_FACTS) + len(EXTRA)}")

# DD4: if the two actors are merged into a single role
POSSIBLE = [(a, s) for a in ACTORS for s in EXTERNAL_FACTS]


def merged(links, internal, owner):
    """Single-actor diagram: only 'is some actor linked' per scenario."""
    return (("User",), tuple(EXTERNAL_FACTS),
            tuple(any(b[1] == s for b in links) for s in EXTERNAL_FACTS),
            tuple(owner[s] for s in EXTERNAL_FACTS))


target = merged(REAL_LINKS, INTERNAL_FACTS, REAL_OWNER)
count = 0
for link_mask in product((0, 1), repeat=len(POSSIBLE)):
    links = [b for b, m in zip(POSSIBLE, link_mask) if m]
    for internal_mask in product((0, 1), repeat=len(INTERNAL_FACTS)):
        internal = [i for i, m in zip(INTERNAL_FACTS, internal_mask) if m]
        if merged(links, internal, REAL_OWNER) == target:
            count += 1
print()
print("if the two actors are merged into a single role:")
print("  symbols", symbol_count(REAL_LINKS), "-> 8 | collapse", BASELINE, "->", count,
      "| ratio", count // BASELINE, "x")
SYSTEM  : 20 facts = 2 actors + 3 scenarios + 5 links + 3 ownerships + 7 internal steps
NOTATION: 9 symbols
COST    : 512 systems collapse into the same diagram

  intentionally omitted (notation cannot carry) : 7 internal steps
  inadvertently dropped (could have been carried): 2 links
     not drawn: IntakeClerk -> opening a work order
     not drawn: IntakeClerk -> picking up
  drawn diagram             : symbols 9 | collapses to 512
  all five links drawn      : symbols 11 | collapses to 128 <- BASELINE of the notation

what happens to the baseline when a field is dropped
field set                    symbols  collapse  bits carried
actor+scenario+link+boundary       11       128              0
link dropped                        6      4096              5
boundary dropped                   10      1024              3
both dropped                        5     32768              8

if two internal steps are drawn as an included use case:
  symbols 11 -> 15 | collapse 128 -> 32
  cost: number of actorless scenarios 0 -> 2 | external-fact ratio 3/3 -> 3/5

if the two actors are merged into a single role:
  symbols 11 -> 8 | collapse 128 -> 3456 | ratio 27 x

Three numbers stand side by side: the system has 20 facts, the notation has 9 symbols, the cost is 512 systems. Five hundred twelve distinct repair shops look identical through this diagram; someone looking at the diagram cannot know which one they are looking at.

Two Distinct Losses

The number five hundred twelve comes from two different sources, and they are useless unless kept apart.

Intentionally omitted facts. None of the seven internal steps find a place in the notation’s information set. The use case notation has no field for writing an internal step; even if it did, it would not be written, because this notation’s reason for existing is to close off the system’s inside and show its outside. This loss binds the type, not the drafter: every use case diagram drops these seven facts, no matter how carefully it is drawn.

Inadvertently dropped facts. The intake clerk’s two links were of a kind the notation could carry — an actor–scenario link, the third item on the list. They were not written. This loss binds the drafter, not the type, and can be fixed without drawing a different diagram.

The difference between the two cannot be seen by looking at the diagram. The reader sees the same thing in both cases: information that is not there. Whether a piece of information is missing on purpose or by carelessness can only be shown by holding the system itself and comparing it against the notation. This is why a loss cannot be known as intentional until it is counted; an uncounted gap is not a defended gap, only an unseen one.

Baseline Ambiguity Comes with the Type

When the two links are written, the ambiguity drops from 512 to 128 — a factor of four. The entire drop is credited to the drafter, because the price paid is only two symbols — 9 symbols becomes 11.

The number 128 does not go any lower from there. This is the use case notation’s baseline ambiguity: each of the seven internal steps either exists in the system or does not, the notation writes none of them, and so 27=1282^7 = 128 distinct systems collapse into the same diagram. The baseline is a number that does not change with drafting skill; it comes with the type.

There are two numbers to look at when evaluating a notation. The baseline states how much of the chosen type refuses, from the start, to show. The gap between the baseline and the actual ambiguity states how much the drafter lost unnecessarily. The first is a design decision, the second is a defect, and they cannot be told apart when referred to by a single number.

A Symbol Is Not Always One Bit

The middle table drops the fields one at a time and measures where the baseline lands. Dropping the five link symbols sends the baseline from 128 to 4096: five symbols were carrying 5 bits. Exactly one bit per link, because each link either exists in the system or does not.

The boundary does not behave this way. Dropping the single boundary symbol sends the baseline from 128 to 1024 — one symbol was carrying 3 bits. The reason: the boundary does not state a single fact but the ownership of all three scenarios at once. In a diagram where the boundary is not drawn, the reader cannot know whether opening a work order is the workshop’s own work or a neighboring system’s; three unknowns remain for three scenarios.

This differs from the ratio measured in the structural notation, where every added symbol carried exactly one bit of ambiguity, because every symbol there wrote a single binary field. In the use case notation, the number of bits a symbol carries depends on how many facts that symbol fixes at once. When both are dropped, the baseline rises to 32768 and the notation drops to 5 symbols: what remains is nothing but two actor names and three scenario names, and 8 bits of information have been lost. The rule that follows is that counting symbols alone is not a measure by itself; what should be counted is not the symbol but the fact the symbol fixes.

The Count of Merging Roles

The same measure also works in the opposite direction. If the customer and the intake clerk are merged into a single user role (DD4), the diagram simplifies: 11 symbols becomes 8, the link count drops from five to three, the drawing shrinks. Ambiguity, on the other hand, rises from 128 to 3456 — a factor of 27.

The number twenty-seven can be read directly. The single-actor diagram states, for each scenario, only that “some user is linked to it,” not which role is linked. For each of the three scenarios, at least one of the two roles has to be linked, which gives 221=32^2 - 1 = 3 possibilities per scenario, and 33=273^3 = 27 for three scenarios. Multiplied by the 128 of the seven internal steps, this gives 3456.

Here the justification for keeping the roles separate is not a matter of style but a measured quantity of information. Three symbols were saved, and roughly 4.75 bits of information were given up in exchange. The question to ask when simplifying a notation is not whether the drawing looks cleaner, but which facts have become indistinguishable.

Bringing an Internal Step into the Notation

There is exactly one way to bring the ambiguity below the baseline: make the untransportable fact transportable. Generating a document and computing cost can be drawn as second-level scenarios included inside the scenario (DD3). The number drops immediately: the baseline goes from 128 to 32, two bits are gained, and the notation rises from 11 symbols to 15.

The cost is not the symbol count. The two newly drawn scenarios have no actor link at all — no actor starts them, because both are the system’s own work. The number of actorless scenarios rises from 0 to 2. The ratio of external facts in the diagram drops from 3/3 to 3/5: only three of the diagram’s five scenarios are now started from outside the system.

What this means is that the system boundary no longer separates anything. What the boundary carried was the information “the inside is closed, the outside enters through these three doors”; once the internal steps move to the same side of the door, that information becomes void. Ambiguity dropped by a factor of four, but what the notation says changed — it no longer describes the face visible from outside, but part of the work itself. The gain is 2 bits and can be counted; the loss is the meaning of a field, and it can only be counted once it is noticed.

Summary

  • The use case notation carries four fields: actor, scenario, actor–scenario link, system boundary. Order, condition, internal step, and data are not in this list.
  • Of the workshop’s 20 facts (2 actors, 3 scenarios, 5 links, 3 ownerships, 7 internal steps), the drawn diagram writes 9 symbols, and 512 distinct systems collapse into the same diagram.
  • Five hundred twelve comes from two sources: 7 internal steps the notation cannot carry, and 2 links it could have carried but that were not drawn. The two cannot be told apart by looking at the diagram.
  • Once the two missing links are drawn, ambiguity drops to 128; 128 is the notation’s baseline ambiguity, it comes with the type, and it does not change with drafting skill.
  • The five link symbols carry 5 bits, the single boundary symbol carries 3 bits: how many bits a symbol carries depends on how many facts that symbol fixes at once.
  • Merging two actors into a single role brings 11 symbols down to 8 but raises ambiguity from 128 to 3456: a factor of 27. Drawing two internal steps as an included scenario brings the baseline down to 32 but raises the number of actorless scenarios from 0 to 2 and drops the external-fact ratio from 3/3 to 3/5.

Next Step

The use case notation deliberately closed off the inside of the system, and the cost of that was counted. The next notation does the opposite: it lifts the lid and shows the inside, laying messages out along a time axis. At first glance this looks like a choice that zeroes out the loss — every message, every participant, every sequence number is written.

The next lesson measures why this is a misreading. The workshop’s flow, because of the two branches of allocation, the two branches of finding a technician, the variable number of line items, and optional document generation, produces 24 distinct traces. A sequence diagram is, by definition, a single trace; its coverage is 1/24, that is, 0.0417. Four diagrams make 0.1667. The lesson counts where the alternative-fragment and loop-fragment notations move this ratio, and at what cost raising the ratio to 1.0 comes.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close