2.2 Structural Models: What Is Connected to What?
A structural model suppresses behavior to expose components, boundaries, and relationships. It says what parts exist and which may depend on which, and it leaves time out. Consider document mutation in DocAble — the running accessibility service — as the first example.
DocAble supports several structured document formats through libraries powerful enough to alter their underlying object structures. Those libraries are necessary, but they sit behind a deliberate architectural boundary. Most remediation logic works through DocAble's structured document abstractions rather than reaching directly into the format-specific implementation.
MODEL CARDDocument-mutation model · Structural
- Engineering question — Where is document mutation supposed to occur?
- Model — a layered structural relation: remediation logic operates through a structured document model, which is realized through a format-specific implementation over DOCX, PPTX, XLSX, and PDF.
- Property — the intended architecture places format-specific mutation behind one sanctioned seam; ordinary remediation logic is not intended to bypass it.
- Quality attribute — architectural integrity and modifiability.
The useful model is small. Figure 2.2-1 draws it: remediation logic operates through a structured document model, which is realized through a format-specific implementation, which finally touches the concrete DOCX, PPTX, XLSX, and PDF formats.
The model leaves almost everything else out. It says nothing about queues, retries, deployment, user sessions, cost, or the mechanics of individual repairs. Those facts are real but irrelevant to this question. The model deliberately preserves only the architectural boundary that matters here.
Note that the model only states that relation; it does not enforce it. Part III returns to the same relation when a static control detects code that bypasses the seam. The model supplies the architectural claim; the mechanism gives that claim authority.
A structural model carries the opposite risk too. The code moves and the drawing does not, so the boundary it shows quietly stops matching the system. That gap between a model and the system it describes is model drift. Later sections make the correspondence explicit by deriving, generating, or tracing between model and implementation.
The reduction is the point. Figure 2.2-2 shows what the model keeps and what it drops: a territory crowded with UI, queues, workers, retries, storage, and deployment collapses to the one seam the question is about.
The engineering question — where may document mutation occur? — is preserved; everything the question does not need is gone.
The document-mutation model asked where one kind of change belongs. A second structural question reaches further inside the system: what computations make up remediation, and how do they depend on one another? DocAble runs a document through a collection of registered remediation passes. A pass may detect a condition, produce information a later pass uses, decide whether remediation should proceed, apply a bounded change to the intermediate representation, or edit a larger region directly. That structure exists in the implementation whether or not a model names it. The first reduction is the obvious one: treat each registered pass as a node, and suppress the statements, library calls, and loops inside it.
MODEL CARDRemediation-computation model · Structural
- Engineering question — What consequential computations make up remediation, and how does information move among them?
- Model — a static directed graph whose nodes are registered computations and whose typed edges represent declared composition among them.
- Property — declared data dependencies and control dependencies are distinguishable and consistent with pipeline ordering; each modeled computation also states whether its effect is bounded as a typed patch, unbounded as a direct edit, or read-only.
- Quality attribute — analyzability, modifiability, and architectural integrity.
An early version of the graph held 113 nodes and two edges. The nodes were already meaningful, because the pass registry gave DocAble a stable decomposition into computations; the two edges were relationships someone had written down by hand. That asymmetry looks obvious once stated, but it was not obvious in the running system, which had a good answer to what computations exist and almost none to how they compose. The implementation knew more than the model. Data moved between passes, signals gated them, and passes mutated shared state, but that knowledge lived heterogeneously in code rather than in one explicit model of composition.
The mismatch does not establish that the model should have existed from the start. The pass decomposition itself emerged while the remediation system was being built, and some implementation freedom was useful while that structure was still shifting. By this point, though, composition had grown hard to reason about informally. The sparse graph was not evidence of missing documentation. It showed that the system had reached a point where a stronger representation could help us understand the territory.
The next move was to derive the missing relationships from what each pass actually reads and writes. That raised a second question: what should an edge mean? A first sweep found 33 candidate producer–consumer edges, and typing them changed the picture. Twenty-two were values a pass read only inside a routing predicate (a ShouldRun or IsRouted decision) to choose whether it executed at all; those values never reached the pass's remediation output. Treating all 33 as one kind of dependency would have produced a graph dominated by routing. Two relations were hiding inside one: a value another computation consumes to produce its result, and a value another computation consults only to decide whether to run. Both are dependencies, and they are not the same dependency.
The model was refined to keep them apart. Each PDF pass declares three sets over a typed facet vocabulary: Produces, Consumes, and ConsumesForControl. The graph projects a data edge from a produced facet that another pass consumes, and a control edge from a produced facet that another pass consumes for control. Re-typing the 33 candidates partitions them: 22 become CONTROL_GATE edges, and the remaining 11 carry payload — 10 DATA_FLOW edges plus one CROSS_SERVICE edge that preserves composition across a service boundary. The 11 payload-bearing edges stay distinct from the 22 control gates.
Figure 2.2-3 shows both moves in one picture: the sparse model as first built, and the typed partition that separated the payload-bearing edges from the control gates.
We did not begin with an ontology that distinguished data flow from control gating and then encode it. It emerged while we tried to construct a model that could answer the engineering question. The representation did two jobs at once: it captured structure we already knew, and it exposed a distinction we had not previously expressed uniformly.
The same exercise exposed a second dimension hidden by a bare node: how bounded each computation's effect is. Passes do not change the document the same way. Some produce a well-scoped typed patch onto the intermediate representation; some mutate document state directly; some only analyze or validate. The model records that as a mutation kind across 68 pass sites: 15 are TypedPatchProducer passes with a bounded typed effect, 40 are DirectEditor passes whose mutation is not yet reduced to a typed patch, and 13 are ReadOnly. DirectEditor is not a defect. The model says something narrower: the effect of those passes is not currently bounded by the typed-patch abstraction. Some of that freedom may be appropriate and some may later prove expensive; the classification makes the question inspectable instead of leaving it tacit. A model need not remove every implementation choice, but it should make it possible to distinguish deliberately preserved freedom from an unexamined choice.
We had modeled the computations before we modeled their composition. The node projection worked because the implementation already carried stable computational units. Modeling their composition then did two further things: it separated data flow from control gating, and it made effect boundedness explicit where an untyped node had hidden it.
The graph is deliberately static. It says nothing about which pass ran in a given job, how long it took, what it cost, whether it retried, or which model fingerprint took part. Those questions belong to an execution model or an execution record. DocAble already records part of that runtime history — a per-session log of the mutations a remediation actually made — but it does not yet join that history to the static computation graph. That record answers a different question, what happened to this artifact, and belongs with the provenance model later in this Part. The two could eventually be joined through shared computation identity; today they are separate by design.
The next chapter adds time: what states a thing occupies, and the orderings it is allowed to move through.