---
title: 'Architectural Views'
source: 'https://academia.sh/en/courses/architectural-documentation/architectural-views'
course: 'Architectural Decisions and Documentation'
language: en
updated: '2026-08-23T07:01:04+00:00'
license: 'CC BY-SA 4.0'
---

# Architectural Views

Applying a question set to the logical, process, development, and physical views: how many questions each view answers, the class of question no view answers, the repetition where two views answer at once, and how many questions are missed by settling for a single view.

The previous topic turned decisions into objects: each decision's context, the alternative it ruled
out, the risk it carries, and the debt it leaves behind are all written down. The regional library
network's record stack can now say "the external catalog is reached through a single connector" or
"notifications are written to a queue." But this stack is a record of individual decisions. Someone
arriving six months later who reads through all of them learns the list of decisions, not the
picture of the system. The answer to "what happens when a loan request comes in" does not sit whole
inside any one record; its pieces are scattered across ten separate records.

A view exists to fill that gap. A **view** is the system seen from a single axis of concern; it
carries the information that is meaningful on that axis and nothing else. The common split is four:
the **logical view**, the **process view**, the **development view**, and the **physical view**.
This lesson defines all four not by their drawings but by **the information items they carry**, and
measures them with a single question: how many of the questions someone asks later does this view
answer.

## A View Is a Set of Information

No diagram is drawn. The structure underneath what would be drawn is modeled: a view is a container
that can carry certain kinds of information items. The logical view carries modules,
responsibilities, dependencies between modules, interfaces, and data entities — but not which
machine a module runs on. The process view carries running processes, call order, points of
concurrency, and queues — but not the source directory structure. The development view carries the
source tree, build units, team ownership, and external dependencies. The physical view carries
nodes, network links, placement, and capacity.

The item lists are chosen by hand (**VM1**); the boundaries might be drawn a little differently on
another network. The selection itself is an input to the measurement, not its result.

The question set is also a data structure (**VM2**). Twenty-four questions, of the kind someone
actually asks six months later: where does a rule live, which process handles a request, which
directory holds a piece of code, which node runs a service. The information items required for each
question's answer are written down. The rule is a single sentence (**VM3**): **a view answers a
question only if it carries every item the answer needs.** Carrying half of it is not an answer; the
reader has to find the rest somewhere else.

```js
// views/model.mjs — MODEL regional library network: the information items carried by
// the four views and the question set someone asks six months later. Not a real
// institution; item lists and questions are chosen by hand.

// A view carries only the items on its list (VM1).
export const VIEW = {
  logical: ["module", "responsibility", "module-dependency", "interface", "data-entity"],
  process: ["module", "interface", "process", "call-order", "concurrency", "queue"],
  development: ["module", "source-tree", "build-unit", "team", "external-dependency"],
  physical: ["node", "network-link", "placement", "capacity", "build-unit"],
};
export const VIEWS = Object.keys(VIEW);

// QUESTION — needs: the information items required for an answer (VM2). A view answers
// a question only if it carries every needed item (VM3).
export const QUESTION = [
  { id: "S01", text: "which module holds the loan-period rule", needs: ["module", "responsibility"] },
  { id: "S02", text: "does the penalty calculation depend on the member registry", needs: ["module", "module-dependency"] },
  { id: "S03", text: "what interface does the catalog connector expose", needs: ["module", "interface"] },
  { id: "S04", text: "what is the relationship between a member and a loan record", needs: ["data-entity"] },
  { id: "S05", text: "what named parts exist in the system", needs: ["module"] },
  { id: "S06", text: "which process handles a reservation request", needs: ["module", "process"] },
  { id: "S07", text: "which process consumes the notification queue", needs: ["queue", "process"] },
  { id: "S08", text: "what gets locked if two branches check out the same copy at once", needs: ["concurrency"] },
  { id: "S09", text: "what order do calls go in during the checkout flow", needs: ["call-order"] },
  { id: "S10", text: "is the call from the kiosk front asynchronous", needs: ["call-order", "queue"] },
  { id: "S11", text: "which directory holds the penalty calculation code", needs: ["source-tree"] },
  { id: "S12", text: "which team owns this module", needs: ["module", "team"] },
  { id: "S13", text: "how many separate build units are there", needs: ["build-unit"] },
  { id: "S14", text: "which external library does the catalog connector use", needs: ["module", "external-dependency"] },
  { id: "S15", text: "which node does the loan service run on", needs: ["placement", "node"] },
  { id: "S16", text: "what kind of link connects the branch front to the center", needs: ["network-link", "node"] },
  { id: "S17", text: "what is the central node's capacity", needs: ["node", "capacity"] },
  { id: "S18", text: "which build unit is placed on which node", needs: ["build-unit", "placement"] },
  { id: "S19", text: "which node's release does the team owning the penalty calculation affect", needs: ["team", "placement"] },
  { id: "S20", text: "which call in the checkout flow goes over the network", needs: ["call-order", "network-link"] },
  { id: "S21", text: "which data entity resides on which node", needs: ["data-entity", "placement"] },
  { id: "S22", text: "what is the responsibility of the module holding the lock", needs: ["concurrency", "responsibility"] },
  { id: "S23", text: "how many processes does releasing a build unit restart", needs: ["build-unit", "process"] },
  { id: "S24", text: "whose job is it when the reservation queue backs up", needs: ["queue", "team"] },
];

export const ITEMS = [...new Set(Object.values(VIEW).flat())].sort();
export const answers = (v, q) => q.needs.every((i) => VIEW[v].includes(i));
export const answeringViews = (q) => VIEWS.filter((v) => answers(v, q));
```

## Measurement

The second file applies the question set to the four views, counts answered and unanswered
questions, extracts the repetition, and runs three schemes through the same stream of changes.

```js
// views/measure.mjs — the question set is applied to the four views; answered,
// unanswered, repeated questions and maintenance cost are counted.
import { VIEW, VIEWS, QUESTION, ITEMS, answeringViews } from "./model.mjs";

const col = (s, n) => String(s).padEnd(n);
const abbr = { logical: "log", process: "prc", development: "dev", physical: "phy" };

const measured = QUESTION.map((q) => ({ ...q, a: answeringViews(q) }));
console.log(col("question", 10) + col("needed item(s)", 34) + "answering view(s)");
console.log("-".repeat(72));
for (const q of measured)
  console.log(col(q.id, 10) + col(q.needs.join("+"), 34) + (q.a.length ? q.a.map((v) => abbr[v]).join(",") : "- none -"));

const single = measured.filter((q) => q.a.length === 1).length;
const multi = measured.filter((q) => q.a.length > 1);
const zero = measured.filter((q) => q.a.length === 0);
console.log(`\n${QUESTION.length} questions: single view ${single}, multiple views ${multi.length}, none ${zero.length}`);
console.log(`the four views together answer ${QUESTION.length - zero.length}/${QUESTION.length} questions`);

console.log("\nview         answered  missed     most frequent missing item");
console.log("-".repeat(62));
for (const v of VIEWS) {
  const missed = measured.filter((q) => !q.a.includes(v));
  const counts = {};
  for (const q of missed) for (const i of q.needs.filter((x) => !VIEW[v].includes(x))) counts[i] = (counts[i] || 0) + 1;
  const top = Object.entries(counts).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 3);
  console.log(col(v, 13) + col(QUESTION.length - missed.length, 10) + col(missed.length, 11) + top.map(([i, n]) => `${i}(${n})`).join(" "));
}

// ---- repetition: how many views repeat an item ----
const viewCount = (i) => VIEWS.filter((v) => VIEW[v].includes(i)).length;
const totalWrites = ITEMS.reduce((t, i) => t + viewCount(i), 0);
console.log(`\n${ITEMS.length} distinct information items are written ${totalWrites} times; ${totalWrites - ITEMS.length} of those writes repeat`);
console.log("repeated item: " + ITEMS.filter((i) => viewCount(i) > 1).map((i) => `${i} x${viewCount(i)}`).join(", "));
console.log("repeated question: " + multi.map((q) => `${q.id}(${q.a.length})`).join(", "));

// ---- cross-mappings the unanswered questions need ----
const pairName = (a, b) => [a, b].sort().join(" <-> ");
const mappings = [...new Set(zero.map((q) => pairName(q.needs[0], q.needs[1])))];
console.log(`\n${zero.length} unanswered questions need ${mappings.length} cross-mappings:`);
for (const q of zero) {
  const owners = q.needs.map((i) => VIEWS.filter((v) => VIEW[v].includes(i)).map((v) => abbr[v]).join("/"));
  console.log(`  ${q.id} ${col(pairName(q.needs[0], q.needs[1]), 36)}(${owners.join(" and ")})`);
}

// ---- maintenance: 200 changes across three schemes ----
// WEIGHT — how often each information item changes (VM4); chosen by hand
const WEIGHT = {
  module: 10, responsibility: 8, "module-dependency": 12, interface: 9, "data-entity": 6,
  process: 5, "call-order": 7, concurrency: 3, queue: 4,
  "source-tree": 6, "build-unit": 4, team: 3, "external-dependency": 5,
  node: 2, "network-link": 2, placement: 3, capacity: 2,
};
function prng(seed) { let s = seed >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const rand = prng(70321); // seed is visible; the same seed gives the same sequence
const totalWeight = ITEMS.reduce((t, i) => t + WEIGHT[i], 0);
const pickItem = () => { let r = rand() * totalWeight; for (const i of ITEMS) if ((r -= WEIGHT[i]) < 0) return i; return ITEMS.at(-1); };

const N = 200;
let singleWrites = 0, fourWrites = 0, fourRepeats = 0, mappingRefresh = 0;
for (let i = 0; i < N; i++) {
  const item = pickItem();
  if (VIEW.process.includes(item)) singleWrites++;
  const k = viewCount(item);
  fourWrites += k; fourRepeats += k - 1;
  mappingRefresh += mappings.filter((m) => m.split(" <-> ").includes(item)).length;
}
const bestSingle = VIEWS.map((v) => [v, measured.filter((q) => q.a.includes(v)).length]).sort((a, b) => b[1] - a[1])[0];
console.log(`\n${N} change events, best single view = ${bestSingle[0]}`);
console.log(col("scheme", 26) + col("answers", 8) + col("writes", 8) + "writes per answer");
console.log("-".repeat(64));
const row = (name, ans, writes) => console.log(col(name, 26) + col(`${ans}/${QUESTION.length}`, 8) + col(writes, 8) + (writes / ans).toFixed(2));
row("single view", bestSingle[1], singleWrites);
row("four views", QUESTION.length - zero.length, fourWrites);
row("four views + mapping", QUESTION.length, fourWrites + mappingRefresh);
console.log(`of the four views' ${fourWrites} writes, ${fourRepeats} write the same fact a second time`);
console.log(`${mappings.length} mappings refresh ${mappingRefresh} times across ${N} changes, opening up ${zero.length} questions in return`);
```

```
question  needed item(s)                    answering view(s)
------------------------------------------------------------------------
S01       module+responsibility             log
S02       module+module-dependency          log
S03       module+interface                  log,prc
S04       data-entity                       log
S05       module                            log,prc,dev
S06       module+process                    prc
S07       queue+process                     prc
S08       concurrency                       prc
S09       call-order                        prc
S10       call-order+queue                  prc
S11       source-tree                       dev
S12       module+team                       dev
S13       build-unit                        dev,phy
S14       module+external-dependency        dev
S15       placement+node                    phy
S16       network-link+node                 phy
S17       node+capacity                     phy
S18       build-unit+placement              phy
S19       team+placement                    - none -
S20       call-order+network-link           - none -
S21       data-entity+placement             - none -
S22       concurrency+responsibility        - none -
S23       build-unit+process                - none -
S24       queue+team                        - none -

24 questions: single view 15, multiple views 3, none 6
the four views together answer 18/24 questions

view         answered  missed     most frequent missing item
--------------------------------------------------------------
logical      5         19         placement(4) build-unit(3) call-order(3)
process      7         17         placement(4) build-unit(3) node(3)
development  5         19         placement(4) call-order(3) node(3)
physical     5         19         module(7) call-order(3) process(3)

17 distinct information items are written 21 times; 4 of those writes repeat
repeated item: build-unit x2, interface x2, module x3
repeated question: S03(2), S05(3), S13(2)

6 unanswered questions need 6 cross-mappings:
  S19 placement <-> team                  (dev and phy)
  S20 call-order <-> network-link         (prc and phy)
  S21 data-entity <-> placement           (log and phy)
  S22 concurrency <-> responsibility      (prc and log)
  S23 build-unit <-> process              (dev/phy and prc)
  S24 queue <-> team                      (prc and dev)

200 change events, best single view = process
scheme                    answers writes  writes per answer
----------------------------------------------------------------
single view               7/24    85      12.14
four views                18/24   280     15.56
four views + mapping      24/24   394     16.42
of the four views' 280 writes, 80 write the same fact a second time
6 mappings refresh 114 times across 200 changes, opening up 6 questions in return
```

## Answered and Unanswered

The four views together answer **eighteen** of the twenty-four questions. Fifteen have their answer
in a single view, three have their answer in more than one view. Six questions find no answer in any
view.

The unanswerable six share a common trait, and this trait is the lesson's real finding: **each one
asks for two items to be combined that live in two different views.** The question of which node's
release the team owning the penalty calculation affects cannot be answered without setting the
development view's team information next to the physical view's placement information. Which call
in the loan flow goes over the network asks for the process view's call order to be mapped onto the
physical view's network link. How many processes releasing a build unit restarts is a mapping
between the development and process views.

This does not come from the four views being poorly chosen. Because views are split by definition
along an axis of concern, **the class of question that falls between axes stays open.** No matter
how well the four views are written, these six questions will not be sitting there; they need something
separate — a mapping between views. The six separate mappings the six questions call for are listed
at the end of the output.

Counting the unanswered question is this course's most valuable measure, because someone reading the
document who cannot find an answer to their question does not report it to anyone; they look inside
the code, ask someone, or guess. If the count is not kept, the gap stays invisible.

## What a Single View Misses

The table below shows what each view does on its own. The process view does best: it answers
**seven** of the twenty-four questions and misses seventeen. The logical, development, and physical
views each answer five questions and miss nineteen.

The "most frequent missing item" column names the **class** of the missed questions. Used alone, the
logical view most often needs placement (4 questions), call order (3), and node (3) information —
that is, what it misses is the runtime and placement class. The physical view's gap is entirely
different: module information is missing in seven questions. The physical view knows where something
runs; it does not name what runs.

The practical conclusion here is this: if a system's document consists of a single view, the
questions it misses do not scatter at random; they're **always from the same class**. A document that
settles for the logical view systematically leaves placement and runtime questions unanswered; a
document that settles for the physical view leaves the question "what runs on this node" unanswered.

## The Cost of Repetition

Three questions are answered in more than one view. At first glance this looks good — information
findable from two places. The cost sits on the item side: seventeen distinct information items are
written into views twenty-one times. Four writes are repeats. The module identity repeats in three
views, the interface in two views, the build unit in two views. When a module's name changes, it has
to change in three places; if one of the three is forgotten, two views contradict each other.

The table below counts this across a stream of changes. How often items change comes from a
hand-picked weight table (**VM4**); a generator seeded with 70321 produces two hundred change events.
The same seed gives the same sequence.

The four-view scheme asks for **280 writes** against two hundred changes; **80** of those write the
same fact a second or third time. In return, eighteen questions get answered: 15.56 writes per
answer. The single-view scheme settles for 85 writes but answers only seven questions: 12.14 writes
per answer. Once six cross-mappings are added on top of the four, the whole set gets answered; the
mappings refresh 114 times across two hundred changes and the writes per answer climb to 16.42.

The way to read the ranking is this: as scope grows, cost per answer grows too, but **the growth is
slow.** While the number of answers climbs from seven to twenty-four — more than triple — the writes
per answer climb from 12.14 to 16.42, about a third more. The cheapness of a single view is an
illusion: what's cheap is not the writing, it is that the seventeen unwritten questions have been
handed off to the reader. These numbers depend on the model's inputs; if the weight table gave
placement and node items a higher value, the physical view's share would grow, but the ranking
would not change.

## Summary

- A view is the system seen from a single axis of concern and is defined not by its drawing but by
  the set of information items it carries; the item lists of the logical, process, development, and
  physical views are distinct and overlap in only three items.
- Across the twenty-four-question set, the four views together answer 18 questions; 15 questions
  find their answer in a single view, 3 in more than one view, 6 in none.
- All six unanswered questions ask for items from two different views to be combined; this gap comes
  not from poorly chosen views but from their being split by axis, and it calls for six
  cross-mappings.
- Settling for a single view, the best case is 7/24 (the process view); the missed questions are not
  random but from the same class — the logical view misses placement and runtime questions, the
  physical view misses module identity in seven questions.
- Seventeen information items are written into views 21 times, 4 writes being repeats; with a
  generator seeded at 70321, across 200 changes the four views ask for 280 writes, 80 of them a
  second write of the same fact. Writes per answer are 12.14 for a single view, 15.56 for four
  views, and 16.42 once mappings are added.

## Next Step

The four-view scheme cut the system along its **axis of concern**: what, when, whose code, where.
One axis stayed uncut. In this model the logical view carries every module in a single list; someone
asking how the network looks from outside and someone asking where a function sits inside a module
have to look at the same list. The first reads far more than they were looking for; the second never
finds the detail they wanted. The next lesson models the same system at four scale levels from
context down to code, and measures: how many nodes and edges per level, at which level a question
gets answered, and how many unnecessary nodes someone looking at the wrong level reads.
