The Physical View

The Development view mapped how the source is organized. The Physical view — Kruchten's deployment view — maps where the built software actually runs: which process lands on which host, across which network boundaries, under what per-host cost and load policy. It is the placement lens. A model that is functionally correct can still be mis-deployed, and only a view that names where things run catches it.

One general type anchors the view, and it is the honest exception in this zoo. A performance and cost model models the computation itself — what it costs to run, what it costs to move data, whether to compute locally or remotely, whether to cache or recompute. The next section takes that gap head-on: the framework supports the cost view, and one real model embodies the load-and-cost half of it, but the pure-latency slice has no dedicated instance, because no failure has yet justified building one. Three real models carry the view: the invariant-DAG execution policy (the idea this chapter turns on — per-host cost and load rationing), the deployment and tier topology model (placement parity, worked in full below), and the substrate-dependency model (a computed migration blast radius).

The performance/cost view: mostly embodied, one honest gap

The zoo names a performance and cost model as a general type, and the framework supports it. But the catalogue holds only a partial instance, and the book states that flat rather than inventing a model to fill the slot.

The load-and-cost half of the view is embodied — by the invariant-DAG execution policy below. Its Scheduler reads a per-host profile of a concurrency ceiling and a budget, and rations both: it fans work out on an elastic host and serializes it on a scarce one, and it honors a cost gate everywhere but a scarce-resource gate only where a box demands it. That is a real cost model in action — cost-to-run and resource-contention, driven from a declared per-host profile.

What has no dedicated instance is the pure-latency slice: a model of request latency, cache-hit ratios, and compute-local-versus-remote data-movement cost, the kind a performance engineer draws to decide where a computation should run for speed. The framework would carry it the same way it carries the others — a typed profile, invariants over it, a drift gate — but no failure has yet justified building it. The zoo names the gap rather than padding it, on the same discipline the whole harness leads with: model only where a failure lives. A model built ahead of a failure is a diagram that will rot before it is ever checked.

The deployment & tier topology model

The typed statement of where things run — each service's name, its layer, and its tier — from which the deploy scripts and layering lints reason about a declared topology rather than scattered constants, and against which the real deploy table is reconciled.

(a) Quality property it helps assess. Two, both a placement fact that drifts silently the moment a service moves.

(b) Constructs and relations. A frozen record per service, keyed by name.

(c) Visual depiction. The natural diagram is a deployment diagram — the build host that produces the image, and the runtime cluster it deploys into. Reused from the model's appendix Structure slot:

flowchart TB
  subgraph Host ["Build host"]
    T[Test serializer]
    B[Build semaphore]
  end
  subgraph Cluster ["Runtime cluster"]
    Web[Web] --> Q[(Queue)]
    Q --> Wk[Worker]
  end
  Host -->|produces image| Cluster

Accessible description: a build host runs the test serializer and build semaphore and produces the image the runtime cluster deploys, where the web service enqueues onto a queue drained by a worker. The model declares this placement; a parity lint holds it to the real deploy tables.

(d) Invariants, and how they are checked. A bidirectional set-diff and a boundary lint:

InvariantTemporal shapeHow it is checked
Every declared service deploys at the tier the model declares□P (safety)Parity lint, model ⊆ reality: a declared service missing from the deploy table, or at the wrong tier, is a finding.
Every deployed service appears in the model□P (safety)Parity lint, reality ⊆ model: a service in the deploy table the model never declared is a finding.
No layer imports across a boundary the model forbids□P (safety)Cross-layer-import lint over the declared layer graph.

The parity check is a set-diff run both directions against the live service-to-tier map. Read the model's declared tiers, read the real deploy table, and print each mismatch: a declared service whose tier differs from the deploy table's, and every deployed name absent from the model. Neither direction is optional — model ⊆ reality alone would miss a service that shipped without a model row, and reality ⊆ model alone would miss a tier that drifted in the code.

(e) Traceability and derivation direction. Model-from-code. The parity check re-reads the real deploy table and reconciles the declared topology against it, so the running system is the ground truth for what deploys. The join key is the service name, which indexes both the Service record and its row in the deploy table. The model is authoritative for the layer graph it holds — the import lint reads it directly — and reconciled against reality for the tier placement it checks.

Also seen in: Development (a layer boundary is a packaging fact). Rendered in full here.

The invariant-DAG execution policy

A deploy graph whose edges carry a typed intent — correctness, cost-gate, or load — kept host-identical, plus a typed Scheduler that reads a per-host profile and rations load and cost so one host fans work out while a scarce one serializes it.

(a) Quality property it helps assess. Two, both about how a host runs the work it was handed.

(b) Constructs and relations. An edge-intent axis plus a per-host Scheduler.

(c) Visual depiction. The natural diagram is a data-flow — edge intents route, load leaves the graph for the Scheduler, and a per-host profile drives the plan. Reused from the model's appendix Structure slot:

flowchart LR
  Edge[Edge intent] --> Corr[CORRECTNESS<br/>in graph]
  Edge --> Cost[COST_GATE<br/>in graph]
  Edge --> Load[LOAD<br/>banned in graph]
  Load --> Sched[Scheduler]
  Prof[(Per-host profile)] --> Sched
  Sched --> Plan([Execution plan])

Accessible description: a deploy-graph edge carries one of three intents. Correctness and cost-gate edges stay in the graph, host-identical. A load edge is banned from the graph and migrated to the Scheduler, which reads a per-host profile and emits an execution plan — the concurrency and cost-gate decisions for that host. Moving the stress burden between hosts is a profile edit, never a graph edit.

(d) Invariants, and how they are checked. A load-edge ban and a plan derivation:

InvariantTemporal shapeHow it is checked
No deploy edge carries the LOAD intent□P (safety)Load-edge lint reads the graph and the Scheduler's graph-resident intent set; a LOAD edge is a finding.
The deploy graph is identical on every host□P (safety)Graph-parity check: the same graph is emitted for every host; a per-host fork is a finding.
The execution plan is a pure function of the host profile□P (safety)Derive-and-assert: the plan is recomputed from the profile, never hand-stored.

(e) Traceability and derivation direction. Model-from-code. The plan is derived from the per-host profile by a pure function, and a hand-stored plan or a LOAD edge is banned outright. The join key is the host name, which indexes both the HostLoadProfile and the deploy phase that runs under the emitted plan. This model is walked in full, with its Scheduler code, in the Scenarios chapter's join example — here it is the Physical-view resident that the join reaches into.

Also seen in: Process (concurrency rationing is a runtime-dynamics concern) and Scenarios (the join example). Rendered in full in the Scenarios chapter; referenced here for the placement and cost half.

The control↔substrate dependency model

The substrate-dependency model makes each governance mechanism declare, as typed metadata, the substrate assumption it bakes in — bound to one substrate, substrate-aware, or substrate-agnostic. It assesses migration safety: before a cross-cutting substrate change, exactly which mechanisms assume the old substrate? The idea worth carrying is that this turns a grep into a computed blast radius. Joining the declared stances against the topology model on the migration target prints the in-scope mechanisms as a table — a real answer, not a hopeful search that misses the one whose assumption is implicit. A declaration lint requires the stance, so a mechanism that reads the substrate silently cannot hide from the join. The appendix carries its typed schema and the join code in full.

---

The Physical view places the parts and rations their cost. The four views so far are static — they say what the system is, does at once, is packaged as, and runs where. The final view sets them in motion: the scenarios that walk a real goal end to end and validate that the other four actually hold up. That is the +1.

Contents
© James C. Davis, 2026–present