---
title: 'Architecture Levels'
source: 'https://academia.sh/en/courses/architect-role/architecture-levels'
course: "The Architect's Role"
language: en
updated: '2026-08-23T07:01:03+00:00'
license: 'CC BY-SA 4.0'
---

# Architecture Levels

Separating application, solution, and enterprise architecture by decision scope: distributing the same set of decisions across three levels by how many units they operate, the number of decisions each level covers and the number of units it affects, and the counts of uninformed units, repeated decisions, and conflicting implementations when a decision is made at the wrong level.

The previous lesson measured eleven decisions on a single codebase and ranked them all on the same
plane. Yet those decisions are not all made in the same place. Where settings are read from binds
the entire system; the daily report being produced in a separate module does not reach beyond two
units; the kiosk front being a separate deployment unit stays within itself alone.

Architecture levels are the name for this distinction: **application architecture**, **solution
architecture**, and **enterprise architecture**. The three levels are usually described by title.
In this lesson they are defined not by title but by scope, and the scope is counted: how many
deployment units a decision binds.

## Scope Determines the Level

The previous lesson's reach measure was at the module level. The question of level needs one more
mapping: which unit runs which module. A **deployment unit** is a piece that is released
separately, runs separately, and is usually looked after by a separate team; it looks in the same
direction as the bounded context distinction established in the Software Design and Architectural
Principles curriculum's Domain-Driven Design course, but here what draws the boundary is not the
domain but operating responsibility.

A decision's scope is this: the set of units that run at least one of the modules in the decision's
reach (**WA4**). If the scope is one unit, the decision is at the application level; if it binds the
whole network or most of it, it is enterprise; if it falls between the two, it is at the solution
level. "Most of it" is a threshold, and the threshold's source must be written down: here, two-thirds
of the units was chosen (**WA5**).

The first file gives the previous lesson's import graph and decision bodies again; the graph is
repeated so the block runs on its own.

```js
// architecture/model.mjs — MODEL library network codebase: import graph and decision bodies.
// Not a real institution; edges and decision bodies are chosen by hand.
export const IMPORTS = {
  configuration: [], clock: [], logging: ["configuration"],
  "data-access": ["configuration", "logging"], "event-bus": ["configuration", "logging"],
  authentication: ["data-access", "configuration", "logging"], authorization: ["authentication"],
  "catalog-connector": ["configuration", "logging"],
  "catalog-cache": ["catalog-connector", "clock"],
  "loan-rule": ["configuration", "clock"],
  "loan-core": ["loan-rule", "data-access", "event-bus", "clock", "catalog-connector"],
  reservation: ["loan-core", "notification-queue", "data-access"],
  "member-registry": ["data-access", "logging"],
  "member-penalty": ["member-registry", "loan-rule", "clock", "data-access"],
  "branch-inventory": ["data-access", "event-bus"],
  "branch-sync": ["branch-inventory", "event-bus", "catalog-connector", "logging"],
  "notification-queue": ["data-access", "event-bus"],
  "notification-email": ["notification-queue", "configuration"],
  "report-daily": ["data-access", "clock"],
  "report-batch": ["data-access", "branch-inventory", "clock", "logging"],
  "web-front": ["loan-core", "catalog-cache", "member-registry", "reservation", "authentication"],
  "staff-front": ["loan-core", "member-registry", "member-penalty", "branch-inventory", "report-daily",
    "authentication", "authorization"],
  "kiosk-front": ["loan-core", "catalog-cache", "authentication"],
  "batch-job": ["report-batch", "branch-sync", "notification-queue", "notification-email"],
};
export const DECISIONS = [
  { name: "data access goes through a single layer", body: ["data-access"] },
  { name: "settings are read from a single source", body: ["configuration"] },
  { name: "inventory is synced over the event bus", body: ["event-bus", "branch-sync"] },
  { name: "the external catalog is accessed through a single connector", body: ["catalog-connector"] },
  { name: "authentication is consolidated in a single module", body: ["authentication"] },
  { name: "the loan period rule is read from configuration", body: ["loan-rule"] },
  { name: "the clock source is taken from a single module", body: ["clock"] },
  { name: "penalty calculation stays separate from the member record", body: ["member-penalty"] },
  { name: "the daily report is produced in a separate module", body: ["report-daily"] },
  { name: "email notifications go out with a plain-text body", body: ["notification-email"] },
  { name: "the kiosk front is a separate deployment unit", body: ["kiosk-front"] },
];
export const MODULES = Object.keys(IMPORTS);
export function dependentClosure(core) {
  const closed = new Set(core);
  for (let grew = true; grew; ) {
    grew = false;
    for (const m of MODULES)
      if (!closed.has(m) && IMPORTS[m].some((h) => closed.has(h))) { closed.add(m); grew = true; }
  }
  return closed;
}
```

## Measurement

The second file builds the unit mapping, counts each decision's scope, and runs the same set of
decisions across three schemes.

```js
// architecture/level.mjs — the number of units a decision covers, and its spread across three levels
import { DECISIONS, dependentClosure } from "./model.mjs";

// UNIT — deployment units of the MODEL library network; which modules each unit runs
const UNIT = {
  "web-portal": ["web-front"],
  "staff-app": ["staff-front"],
  "kiosk-network": ["kiosk-front"],
  "loan-service": ["loan-core", "loan-rule", "reservation"],
  "member-service": ["member-registry", "member-penalty"],
  "catalog-integration": ["catalog-connector", "catalog-cache"],
  "inventory-service": ["branch-inventory", "branch-sync"],
  "notification-service": ["notification-queue", "notification-email"],
  "report-service": ["report-daily", "report-batch", "batch-job"],
  "shared-infrastructure": ["configuration", "logging", "clock", "data-access", "event-bus",
    "authentication", "authorization"],
};
const UNITS = Object.keys(UNIT);
const U = UNITS.length;
const ENTERPRISE_THRESHOLD = Math.ceil((U * 2) / 3); // chosen rule: two-thirds of the units

const scope = (k) => {
  const reach = dependentClosure(k.body);
  return UNITS.filter((u) => UNIT[u].some((m) => reach.has(m)));
};
const levelName = (n, e = ENTERPRISE_THRESHOLD) => (n === 1 ? "application" : n >= e ? "enterprise" : "solution");

const measurements = DECISIONS.map((k) => ({ name: k.name, n: scope(k).length, set: scope(k) }));
const col = (s, n) => String(s).padEnd(n);
console.log(`${U} units, ${DECISIONS.length} decisions, enterprise threshold = ${ENTERPRISE_THRESHOLD} units\n`);
console.log(col("decision", 60) + col("units", 8) + "level");
console.log("-".repeat(78));
for (const o of [...measurements].sort((a, b) => b.n - a.n)) console.log(col(o.name, 60) + col(o.n, 8) + levelName(o.n));

console.log("");
for (const d of ["enterprise", "solution", "application"]) {
  const g = measurements.filter((o) => levelName(o.n) === d);
  const affected = new Set(g.flatMap((o) => o.set));
  console.log(`${col(d, 13)} decisions=${col(g.length, 4)} units affected=${col(affected.size, 4)} avg. scope=${(g.reduce((t, o) => t + o.n, 0) / g.length).toFixed(1)}`);
}
console.log("threshold sensitivity: " + [6, 7, 8].map((e) =>
  `threshold ${e} -> ${measurements.filter((o) => levelName(o.n, e) === "enterprise").length} enterprise`).join(", "));

// ---- comparison: same set of decisions across three schemes ----
// OPTIONS[k] = the decision's number of reasonable options; the unit that decides independently picks one of them
const OPTIONS = [3, 4, 3, 2, 3, 4, 2, 3, 2, 3, 2];
function prng(seed) { let s = seed >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const rand = prng(48151); // seed is visible; the same seed gives the same numbers

let uninformed = 0, repeated = 0, conflicting = 0;
measurements.forEach((o, i) => {
  const others = o.n - 1;              // scope units other than the decision's owner
  uninformed += others; repeated += others;
  const ownersChoice = Math.floor(rand() * OPTIONS[i]);
  for (let j = 0; j < others; j++) if (Math.floor(rand() * OPTIONS[i]) !== ownersChoice) conflicting++;
});
const consultLocal = 0, consultThreeLevel = measurements.reduce((t, o) => t + o.n - 1, 0), consultCentral = DECISIONS.length * (U - 1);

console.log(`\n${col("scheme", 12) + col("uninformed", 12) + col("repeated", 10) + col("conflicting", 13)}consults`);
console.log("-".repeat(55));
console.log(col("local", 12) + col(uninformed, 12) + col(repeated, 10) + col(conflicting, 13) + consultLocal);
console.log(col("three-level", 12) + col(0, 12) + col(0, 10) + col(0, 13) + consultThreeLevel);
console.log(col("centralized", 12) + col(0, 12) + col(0, 10) + col(0, 13) + consultCentral);

console.log(`\nthe local scheme avoids ${consultThreeLevel} consults, in exchange it gets ${conflicting} conflicting implementations`);
console.log(`break-even: the three-level scheme wins if resolving a conflicting implementation costs more than ${(consultThreeLevel / conflicting).toFixed(2)} consults`);
console.log(`the centralized scheme spends ${consultCentral - consultThreeLevel} more consults than the three-level scheme, while conflicts are 0 in both`);
const soleScope = measurements.filter((o) => o.n === 1).length;
console.log(`of that, ${soleScope * (U - 1)} is spent on the ${soleScope} decision(s) whose scope is a single unit`);
```

```
10 units, 11 decisions, enterprise threshold = 7 units

decision                                                    units   level
------------------------------------------------------------------------------
settings are read from a single source                      10      enterprise
data access goes through a single layer                     9       enterprise
inventory is synced over the event bus                      8       enterprise
the clock source is taken from a single module              8       enterprise
the external catalog is accessed through a single connector 7       enterprise
the loan period rule is read from configuration             5       solution
authentication is consolidated in a single module           4       solution
penalty calculation stays separate from the member record   2       solution
the daily report is produced in a separate module           2       solution
email notifications go out with a plain-text body           2       solution
the kiosk front is a separate deployment unit               1       application

enterprise    decisions=5    units affected=10   avg. scope=8.4
solution      decisions=5    units affected=8    avg. scope=3.0
application   decisions=1    units affected=1    avg. scope=1.0
threshold sensitivity: threshold 6 -> 5 enterprise, threshold 7 -> 5 enterprise, threshold 8 -> 4 enterprise

scheme      uninformed  repeated  conflicting  consults
-------------------------------------------------------
local       47          47        31           0
three-level 0           0         0            47
centralized 0           0         0            99

the local scheme avoids 47 consults, in exchange it gets 31 conflicting implementations
break-even: the three-level scheme wins if resolving a conflicting implementation costs more than 1.52 consults
the centralized scheme spends 52 more consults than the three-level scheme, while conflicts are 0 in both
of that, 9 is spent on the 1 decision(s) whose scope is a single unit
```

## Reading the Distribution

The eleven decisions split into five enterprise, five solution, and one application-level decision.
The average scope of the five enterprise-level decisions is 8.4 units, of the five solution-level
decisions, 3.0 units. The union column is informative too: the five enterprise-level decisions touch
**all** ten units, the five solution-level decisions touch eight.

The threshold's fragility is measured here too. Lowering the enterprise threshold from seven to six
changes no decision, because no decision has a scope of six units; raising it to eight drops the
external catalog connector decision from the enterprise level to the solution level. The threshold
is a choice, not a ranking; which decision outranks which comes from the measurement, the boundary
comes from a rule.

What the distribution actually says is this: no decision belongs to a level by virtue of its subject
matter. Where the clock is read from looks, as a subject, like an application detail; its scope is
eight units, and in this model it falls at the same level as inventory sync. A decision's level
comes not from its subject but from how many units depend on it.

## The Cost of the Wrong Level

The table below runs the same eleven decisions across three schemes. In the **local scheme**, each
decision is made in the unit where its body sits and stays there. In the **centralized scheme**,
each decision is made by consulting every unit. In the **three-level scheme**, each decision is made
at the level of its measured scope and only the units in that scope are consulted.

In the local scheme, 47 in-scope units never hear the decision. Since a unit that does not hear it
still has to get its work done, it makes the same decision on its own: 47 repeated decisions. Not
all repeated decisions clash — for each decision the number of reasonable options is between two and
four (**WA6**), and the unit deciding independently picks one of them. With the generator seeded at
48151, **31** of the 47 repeats came out different from the owner's choice, that is, 31 conflicting
implementations.

The local scheme's gain is that it avoids 47 consults. From here the break-even point is read: if
reconciling a conflicting implementation costs more than **1.52 consults**, the three-level scheme
wins. This is a calculation, not a preference, and the number depends on the model's inputs; as the
option counts grow, conflicting implementations increase and the break-even point drops.

The centralized scheme brings the conflict down to zero too, but with 99 consults — 52 more than the
three-level scheme. Nine of those are spent on a single decision: the kiosk front being a separate
deployment unit has a scope of one unit, and yet nine more units are consulted anyway. Deciding at
the wrong level has two directions, and both have a number: deciding lower than the true level
produces uninformed units and conflicting implementations, deciding higher than the true level
produces wasted consults.

## Summary

- A decision's level is read from its scope, not its subject; scope is the number of deployment
  units that run the modules in the decision's reach.
- The model network has ten units and eleven decisions; when the enterprise threshold is chosen as
  two-thirds of the units (7 units), five enterprise, five solution, and one application-level
  decision result.
- The five enterprise-level decisions touch all ten units (average scope 8.4); the five
  solution-level decisions touch eight units (average scope 3.0).
- In the local scheme, 47 units do not hear the decision, 47 decisions are made again, and with the
  generator seeded at 48151, 31 of these come out different from the owner's choice; the break-even
  point is where a conflicting implementation equals 1.52 consults.
- The centralized scheme brings the conflict to zero but spends 99 consults; nine of those are for
  the single decision whose scope is one unit.

## Next Step

Both lessons treated the decision as something already made: the decision exists, its scope is
measured, its level is determined. Who makes the decision, who it is announced to and how once it is
made, and whether it is actually applied once announced — none of that was asked. Someone carries
out the three-level scheme's 47 consults; that person's job is not only to make decisions. The next
lesson splits the architect's job into three responsibilities — decision, communication, and
oversight — and builds all three as a matrix. What it measures is this: the share of decisions that,
despite having been made and announced, have no counterpart left in the code, and how much that
share changes once oversight is added.
