Lesson 02 / 10
Frameworks
The shared structure of enterprise architecture frameworks — layer separation, view set, governance cycle, and maturity level — and documenting the same enterprise model in a detailed-layered and a lightweight format: how many questions each format answers, how many items it carries, how many items are touched per change, and the questions no format answers at all.
Contents
The previous lesson built the enterprise model and tied alignment to three numbers. The model was produced once, but where it should be written down was never discussed: which layers it splits into, how many separate views it is kept in, who reviews it and how often are all unsettled. Enterprise architecture frameworks claim to fill this gap, and the claim usually becomes a debate — which framework to pick, whether the heavy one or the light one is right. This lesson turns the debate into a measurement. No brand or framework name is written; frameworks are referred to by their shared structure.
The Shared Structure of Frameworks
Whatever their names and details, enterprise architecture frameworks are built from four parts.
Layer separation divides the enterprise into planes that are distinct from one another but mapped onto each other: business capabilities and their owners, systems, data assets, and the infrastructure that carries the systems. The view set is the same model’s cross-sections, each prepared for a different reader. The governance cycle ties down which record is reviewed by whom and how often. The maturity level is a ladder ranking how much of these records the enterprise keeps.
What these four parts share is this: none of them produces information, each tells you where information gets written. A framework can therefore only be measured through the record types it carries. Measurement needs three definitions. An item is a record row looked at independently; fields that fall on the same row are not counted as separate items (EA5). The question set is the twelve questions someone asks later; the measure is borrowed from the Architectural Decisions and Documentation course — a document’s value is the number of questions it answers (EA6). A maturity level is not a title but a predicate tied to the presence of specific record types (EA7).
The first file gives the previous lesson’s enterprise model again; the block is repeated so it can run on its own.
// enterprise/model.mjs — MODEL regional library network: systems, owners, capabilities, data. // Fictional; no real institution, vendor, product, or person is described. export const SYSTEM = { catalog: { owner: "external-provider", budget: "service-fee" }, loan: { owner: "it-department", budget: "internal-development" }, billing: { owner: "it-department", budget: "internal-development" }, membership: { owner: "member-services", budget: "member-services" }, identity: { owner: "municipal-it", budget: "municipality" }, "branch-local": { owner: "branch-management", budget: "branch" }, kiosk: { owner: "branch-management", budget: "branch" }, reporting: { owner: "management-unit", budget: "management" }, archive: { owner: "none", budget: "none" }, }; // CAPABILITY — the business capabilities the enterprise must cover (a list independent of system) export const CAPABILITY = ["material-search", "loan-issuance", "return-intake", "reservation", "membership-enrollment", "member-verification", "fee-calculation", "fee-collection", "inter-branch-transfer", "asset-count", "usage-reporting", "purchase-suggestion", "overdue-notification"]; // COVERS[s] = the capabilities system s claims to cover export const COVERS = { catalog: ["material-search", "asset-count"], loan: ["loan-issuance", "return-intake", "reservation", "overdue-notification"], billing: ["fee-calculation", "overdue-notification"], membership: ["membership-enrollment", "member-verification"], identity: ["member-verification"], "branch-local": ["return-intake", "inter-branch-transfer", "asset-count"], kiosk: ["loan-issuance", "material-search"], reporting: ["usage-reporting"], archive: [], }; // DATA[v] = data asset; which system writes it, which ones read it export const DATA = { "member-record": { writes: ["membership"], reads: ["loan", "billing", "kiosk", "reporting"] }, "identity-match": { writes: ["identity", "membership"], reads: ["loan", "kiosk"] }, "material-record": { writes: ["catalog"], reads: ["loan", "kiosk", "branch-local", "reporting"] }, "copy-status": { writes: ["catalog", "loan", "branch-local"], reads: ["kiosk", "reporting"] }, "loan-transaction": { writes: ["loan", "kiosk"], reads: ["billing", "reporting"] }, "fee-record": { writes: ["billing"], reads: ["membership", "reporting", "kiosk"] }, "penalty-rule": { writes: ["billing"], reads: ["loan", "kiosk"] }, "transfer-request": { writes: ["branch-local"], reads: ["loan", "catalog"] }, "legacy-record": { writes: [], reads: ["archive", "reporting"] }, "count-discrepancy": { writes: ["branch-local"], reads: [] }, }; export const SYSTEMS = Object.keys(SYSTEM); export const owner = (s) => SYSTEM[s].owner; // edge = the directed pair from the system that writes a data asset to the system that reads it export function edges() { const e = new Map(); for (const [v, d] of Object.entries(DATA)) for (const w of d.writes) for (const r of d.reads) if (w !== r) e.set(`${w}->${r}`, [...(e.get(`${w}->${r}`) ?? []), v]); return e; }
Measurement
The second file defines the two formats. The detailed-layered format keeps each record type as a separate item in its own layer; the lightweight format consists of nothing more than a single system table and a capability list, folding owner, budget, and coverage information into the system row’s columns.
// enterprise/framework.mjs — same enterprise model in two framework formats: answer, item, maintenance import { SYSTEM, CAPABILITY, COVERS, DATA, SYSTEMS, owner, edges } from "./model.mjs"; const col = (s, n) => String(s).padEnd(n); const E = edges(); // ITEM[type] = number of rows of that record type in the model enterprise const ITEM = { capability: CAPABILITY.length, owner: new Set(SYSTEMS.map(owner)).size - 1, budget: new Set(SYSTEMS.map((s) => SYSTEM[s].budget)).size - 1, system: SYSTEMS.length, "capability-mapping": Object.values(COVERS).flat().length, data: Object.keys(DATA).length, edge: E.size, carrier: 4, "system-carrier": SYSTEMS.length, view: 4, governance: 4, maturity: 1, }; // FORMAT[f][type] = the independent item that carries that record type; null means the format does not hold it. // Records that fall on the same item do not need separate maintenance (this describes shared structure, not a brand). const own = (l) => Object.fromEntries(l.map((k) => [k, k])); const FORMAT = { "detailed-layered": own(Object.keys(ITEM)), lightweight: { system: "system", owner: "system", budget: "system", "capability-mapping": "system", capability: "capability" }, }; const holds = (f, type) => Boolean(FORMAT[f][type]); const itemCount = (f) => [...new Set(Object.values(FORMAT[f]))].reduce((t, k) => t + ITEM[k], 0); // QUESTION[i] = a question someone asks later; needs = the record types required to answer it const QUESTION = [ ["which system covers which capability", ["capability-mapping"]], ["who owns a system", ["owner"]], ["which capability is covered by no system", ["capability", "capability-mapping"]], ["if a system is shut down, which capability is left uncovered", ["capability-mapping"]], ["which budget pays for a system's cost", ["budget"]], ["which system writes a given data asset", ["data"]], ["which data passes between two systems", ["edge"]], ["which carrier does a system run on", ["system-carrier"]], ["which section of the document was reviewed when", ["governance"]], ["which view documents a given topic", ["view"]], ["how often does a given data asset pass", ["edge-frequency"]], ["who is notified if an edge breaks", ["edge-owner"]], ]; const answers = (f, needs) => needs.every((n) => holds(f, n)); // CHANGE[i][1] = the records that must be updated when the change happens const CHANGE = [ ["a new system goes live", { system: 1, "capability-mapping": 1, edge: 2, carrier: 1, "system-carrier": 1, view: 2, governance: 1 }], ["a system is transferred to another unit", { system: 1, owner: 1, budget: 1, view: 1, governance: 1 }], ["a new reader is added to a data asset", { data: 1, edge: 1, view: 1, governance: 1 }], ]; function touched(f, d) { const group = new Map(); // records that fall on the same item count as one maintenance for (const [type, n] of Object.entries(d)) { const k = FORMAT[f][type]; if (k) group.set(k, Math.max(group.get(k) ?? 0, n)); } return [...group.values()].reduce((t, n) => t + n, 0); } // MATURITY — rungs; each rung requires certain record types const MATURITY = [ ["a system inventory exists", ["system"]], ["every system's owner is recorded", ["system", "owner"]], ["a capability mapping exists", ["capability", "capability-mapping"]], ["data ownership is recorded", ["data"]], ["the impact of a change can be computed", ["data", "edge"]], ]; const level = (f) => { let n = 0; for (const [, needs] of MATURITY) { if (!needs.every((g) => holds(f, g))) break; n++; } return n; }; const formats = Object.keys(FORMAT); console.log(`MODEL enterprise: ${SYSTEMS.length} systems, ${CAPABILITY.length} capabilities, ` + `${Object.keys(DATA).length} data, ${E.size} edges; question set ${QUESTION.length} questions\n`); console.log(col("question", 64) + formats.map((f) => col(f, 20)).join("")); console.log("-".repeat(104)); for (const [q, needs] of QUESTION) console.log(col(q, 64) + formats.map((f) => col(answers(f, needs) ? "answer" : "-", 20)).join("")); console.log(""); console.log(col("format", 20) + col("answer", 8) + col("item", 8) + col("level", 9) + col("item/answer", 13) + "items touched per change"); console.log("-".repeat(100)); for (const f of formats) { const y = QUESTION.filter(([, g]) => answers(f, g)).length; const k = itemCount(f); const d = CHANGE.map(([, o]) => touched(f, o)); console.log(col(f, 20) + col(`${y}/${QUESTION.length}`, 8) + col(k, 8) + col(`${level(f)}/5`, 9) + col((k / y).toFixed(1), 13) + d.join(" + ") + ` = ${d.reduce((t, n) => t + n, 0)}`); } const [d, l] = formats; const ansD = QUESTION.filter(([, g]) => answers(d, g)).length; const ansL = QUESTION.filter(([, g]) => answers(l, g)).length; console.log(`\ndiff: ${itemCount(d) - itemCount(l)} more items -> ${ansD - ansL} more answers = ` + `${((itemCount(d) - itemCount(l)) / (ansD - ansL)).toFixed(1)} items/extra answer`); const untouched = CHANGE.filter(([, o]) => touched(l, o) === 0); console.log(`changes where the lightweight format touches no item: ${untouched.length} ` + `(${untouched.map(([name]) => name).join("; ")})`); const unanswered = QUESTION.filter(([, g]) => formats.every((f) => !answers(f, g))); console.log(`questions no format answers: ${unanswered.length} -> ` + unanswered.map(([q]) => q).join(" / ")); console.log("annual review rounds (capacity sensitivity):"); for (const cap of [20, 40, 60]) console.log(" per round " + col(cap, 4) + "items -> " + formats.map((f) => `${f}=${Math.ceil(itemCount(f) / cap)}`).join(", "));
MODEL enterprise: 9 systems, 13 capabilities, 10 data, 23 edges; question set 12 questions question detailed-layered lightweight -------------------------------------------------------------------------------------------------------- which system covers which capability answer answer who owns a system answer answer which capability is covered by no system answer answer if a system is shut down, which capability is left uncovered answer answer which budget pays for a system's cost answer answer which system writes a given data asset answer - which data passes between two systems answer - which carrier does a system run on answer - which section of the document was reviewed when answer - which view documents a given topic answer - how often does a given data asset pass - - who is notified if an edge breaks - - format answer item level item/answer items touched per change ---------------------------------------------------------------------------------------------------- detailed-layered 10/12 106 5/5 10.6 9 + 5 + 4 = 18 lightweight 5/12 22 3/5 4.4 1 + 1 + 0 = 2 diff: 84 more items -> 5 more answers = 16.8 items/extra answer changes where the lightweight format touches no item: 1 (a new reader is added to a data asset) questions no format answers: 2 -> how often does a given data asset pass / who is notified if an edge breaks annual review rounds (capacity sensitivity): per round 20 items -> detailed-layered=6, lightweight=2 per round 40 items -> detailed-layered=3, lightweight=1 per round 60 items -> detailed-layered=2, lightweight=1
The Difference Between the Two Formats
The detailed-layered format answers ten of the twelve questions and carries 106 items. The lightweight format answers five and carries 22 items. The difference comes down to a single number: 84 more items, 5 more answers — 16.8 items per extra answer. The framework choice is made against this ratio. If an enterprise finds the answer to “which system writes a given data asset” worth the ongoing maintenance of 16.8 items, the detailed format wins; if it does not, the lightweight format wins. This is a calculation, not a preference, and the numbers depend on the size of the enterprise model: as the edge count grows, the detailed format’s item count grows fast, because 23 of its 106 items are edges.
The per-change cost difference is sharper: the same three changes update 18 items in the detailed format and 2 in the lightweight one. There is a caveat inside this. The change “a system is transferred to another unit” touches five items in the detailed format; the system itself does not change, only its owner and budget do. At enterprise scale, most maintenance comes not from technical change but from ownership change.
The third change makes the distinction visible: when a new reader is added to a data asset, the lightweight format touches no item at all. This is the source of the lightweight format’s cheapness, and at the same time its limit. The document does not go stale, because it never made any claim on the matter; it stays silent. A new edge forms in the enterprise, and no one looking at the document can see it.
The maturity level is a shorthand for the same difference: the detailed format reaches five of the five levels, the lightweight format three. A level is not a ranking of success; it is which questions can be answered, reduced to a single number. The lightweight format stops at the fourth level, because it does not keep data ownership at all — and the fifth level, whether the impact of a change can be computed, depends exactly on this course’s unit of measure.
The Question a Framework Does Not Answer
Two questions stay unanswered in either format: how often a given data asset passes, and who is notified when an edge breaks. The reason for this is not the framework choice. The model never carries these fields; because it does not carry them, no layer separation and no view set can produce them. A framework debate does not produce information, it organizes information that already exists — and if there is no information to organize, a more detailed framework only produces more empty items.
The governance cycle’s cost is read from the same place. The number of items that can be worked through per round is a threshold, and the source of that threshold should be written down: this model chose forty items per round (EA8). At this threshold, the detailed format is reviewed in three rounds, the lightweight format in one. When the threshold drops to twenty, the detailed format rises to six rounds; when it rises to sixty, it falls to two. If the review capacity stays below the item count, the document loses currency — that is, the framework itself becomes the source of the drift it measures.
Summary
- Enterprise architecture frameworks are built from four parts regardless of brand differences: layer separation, view set, governance cycle, and maturity level; none of them produces information, each determines where it belongs.
- The same enterprise model answers 10 of 12 questions and carries 106 items in the detailed-layered format; it answers 5 and carries 22 items in the lightweight format.
- The number that decides is items per extra answer: 84 more items buy 5 more answers, that is, 16.8 items/extra answer; maintenance per change is 18 items against 2.
- One change touches no item at all in the lightweight format — a new reader being added. This is the source of the cheapness, and it is also where the document stays silent.
- Two questions go unanswered in either format (a data asset’s frequency and an edge’s owner), because the model never carries those fields; choosing a framework does not fill a missing field.
Next Step
Both lessons treated the enterprise as a static structure: systems, owners, capabilities, records. But a business capability is not a list row; it is a process that unfolds step by step. Between a member walking through the door and taking a material in hand there is a sequence of operations, and not all of these steps happen inside software. The next lesson models a business process step by step and sorts every step into three classes — fully automatic, requiring human judgment, outside the system. What gets measured is where the automation boundary runs, and how many rollbacks come from forcing that boundary, that is, from automating a step that requires human judgment.
To keep your progress and take notes, Log in
My notes
Log in to take notes.