import { DEFINITION_SECTION_ICON, GRAPH_SECTION_ICON } from "#configuration/icons/architecture.icons";
import { DEFINITION_SECTION_ID, GRAPH_SECTION_ID } from "#core/ids/architecture.ids";
import type { Section } from "#types/document.types";
import { TYPESCRIPT_LANGUAGE } from "#configuration/constants/code.constants";
const SYSTEM_SHAPE =
'export interface Component {\n readonly id: ComponentId;\n readonly concern: Concern;\n readonly layer: Layer;\n}\n\nexport interface Relation {\n readonly from: ComponentId;\n readonly to: ComponentId;\n readonly kind: "imports" | "exports" | "registers" | "consumes" | "emits" | "subscribes" | "interfaces";\n}\n\nexport interface Schema {\n readonly invariants: readonly Invariant[];\n readonly evaluate: (graph: SystemGraph) => readonly Finding[];\n}\n\nexport interface Propagation {\n readonly reaches: (change: ComponentId, graph: SystemGraph) => readonly ComponentId[];\n}\n\nexport interface SystemGraph {\n readonly components: readonly Component[];\n readonly relations: readonly Relation[];\n readonly schema: Schema;\n readonly propagation: Propagation;\n}\n\nexport const derivable = (graph: SystemGraph, described: SystemGraph): boolean =>\n graph.schema.evaluate(described).length === 0;';
const MODEL_DIAGRAM =
'flowchart TB\n components["Components · the nodes"]\n relations["Relations · the edges"]\n schema["The evaluative schema · applied to the graph"]\n propagation["The propagation topology · how a change travels"]\n rest["The graph at rest · what the system is"]\n motion["Propagation · the system in motion"]\n healthy{"Is the behaviour derivable from the self-description?"}\n yes["Healthy"]\n fracture["A fracture · dual write, hidden side effect, schema drift, manual-only governance"]\n components --> rest\n relations --> rest\n schema --> rest\n propagation --> motion\n rest --> healthy\n motion --> healthy\n healthy -- yes --> yes\n healthy -- no --> fracture';
const FRACTURE_DIAGRAM =
'flowchart LR\n test{"Can the system observe this value in its own self-description?"}\n dual["Dual write · one fact declared twice"]\n hidden["Hidden side effect · a value nothing reads back"]\n drift["Schema drift · a description the system cannot check"]\n manual["Manual-only governance · a step the description does not contain"]\n closed["Closed · one declaration, derived everywhere"]\n test -- no --> dual\n test -- no --> hidden\n test -- no --> drift\n test -- no --> manual\n test -- yes --> closed';
const DEPENDENCY_DIAGRAM =
'flowchart LR\n subgraph domain["domain"]\n model["order model"]\n policy["pricing policy"]\n end\n subgraph application["application"]\n coordinator["checkout coordinator"]\n store["cart store"]\n end\n subgraph processing["processing"]\n converter["order converter"]\n validator["order validator"]\n end\n subgraph product["product"]\n view["checkout view"]\n end\n view --> coordinator\n coordinator --> store\n coordinator --> converter\n converter --> model\n validator --> model\n validator --> policy\n coordinator --> validator\n policy -. circular dependency .-> coordinator';
const DECLARATION_DIAGRAM =
'flowchart LR\n definition["A definition · what exists"]\n code["Code · how it behaves"]\n derived["A derivation · read from the definition"]\n restated["A restatement · written twice"]\n definition --> derived --> code\n definition -. never .-> restated\n restated -. disagrees the moment either copy moves .-> code';
const GRAPH_SECTION: Section = {
icon: GRAPH_SECTION_ICON,
id: GRAPH_SECTION_ID,
intro: "Every question worth asking about a system is a question about reach, and reach is a walk over a graph. This chapter gives the model the rest of the page evaluates, the four parts the four parts draws, the walk one feature lays out and the test the four fractures applies; derived state on the methodology page is its practice for a tree.",
subsections: [
{
blocks: [
{
application:
"Model a system as a dependency graph before designing any part of it. Name the components and the relation kinds. State the schema as invariants a walk can evaluate over the graph. State how a change propagates, so impact analysis is a traversal rather than a guess. Then ask of every value the system carries whether the system can observe that value in its own self-description, and treat a value that fails the question as a fracture.",
boundary:
"The model is a description of structure, and it says nothing about which components are good. A graph with clean edges and a bad decomposition is still a graph, and the decomposition is judged by the schema applied to it, never by the model that holds it.",
cause: "A system described in prose has parts nobody can enumerate and relations nobody can traverse, so every question about it is answered by recollection.",
decision:
"Model the graph before any part of it, rather than assemble parts and infer the graph afterwards.",
failureMode:
"A limit is declared in two places, a state is written by hand where nothing reads it back, a description goes stale beside the code it describes, and a step is performed by a person, and none of the four is anyone's defect because none of them has a name.",
kind: "lesson",
principle:
"A system is components, relations, an evaluative schema and a propagation topology, and it is healthy when its behaviour is derivable from its self-description.",
problem:
"Software is usually reasoned about as a pile of parts rather than as a graph, so nobody can say what a change reaches or what the system claims about itself.",
validation:
"Pick a component and derive what a change to it reaches, from the graph alone. Then change it and observe what actually moved. The difference between the two sets is the part of the topology nobody has modelled.",
},
{ caption: "the four parts", kind: "mermaid", text: MODEL_DIAGRAM },
],
title: "Components, relations, schema, propagation",
},
{
blocks: [
{
kind: "text",
text: "Which modules a change touches is a walk over the dependency graph. Which of two components knows about the other is the direction of one edge. Whether the whole can be built in one pass is whether the graph is a directed acyclic graph, and a circular dependency is the one shape that makes the answer no.",
},
{
kind: "text",
text: "Traceability, from a requirement to the code that carries it and back, is a path. Modularity, loose coupling and high cohesion are each a statement about how many edges cross a boundary and how many stay inside it. A list can hold every one of those facts and can answer none of the questions, because a list has no edges to walk.",
},
{ caption: "one feature", kind: "mermaid", text: DEPENDENCY_DIAGRAM },
],
title: "Why a graph and not a list",
},
{
blocks: [
{
kind: "text",
text: "Self-describing architecture is the property the health test names, and it is stronger than observability. An observable system can be watched from outside. A self-describing one carries its own description as data it can read, and introspection over that data answers the reach questions without running anything.",
},
{
kind: "text",
text: "The four fractures are the four ways a description and a behaviour come apart. A dual write is one fact declared twice, and it disagrees with itself the moment either copy moves. A hidden side effect is a value the system changes where nothing reads it back, so it can be wrong forever. Schema drift is a description the system cannot check against itself, right on the day it was written. Manual-only governance is a step the description does not contain, so the system behaves differently depending on who performs it.",
},
{ caption: "the four fractures", kind: "mermaid", text: FRACTURE_DIAGRAM },
],
title: "The four fractures",
},
{
blocks: [
{
kind: "text",
text: "Take any system you already have and ask of each fact where it is declared and what reads that declaration. A port written in a manifest and again in a start script is a dual write. A feature toggle flipped by hand in a console is a hidden side effect. A document that names three services where the tree holds four is schema drift. A release checklist a person walks is manual-only governance.",
},
{
kind: "text",
text: "None of the four is a bug in the ordinary sense. All four are fractures in the derivation, and the repair for each is one shape: one declaration, and every other appearance derived from it or deleted.",
},
],
title: "The example is the system you have",
},
],
title: "A system is a graph",
};
const DEFINITION_SECTION: Section = {
icon: DEFINITION_SECTION_ICON,
id: DEFINITION_SECTION_ID,
intro: "A registry declares what variants exist and the code discovers them, a schema declares what a record carries and the code validates against it, a manifest declares what a module is for and the code derives its surface. The split runs through every fact a tree carries, as derivation or restatement draws and the model typed states, and one home on the methodology page is its practice.",
subsections: [
{
blocks: [
{
application:
"Decide for every fact which side owns it. A fact about what exists goes into a definition the code reads, and the code derives everything else from it: the list of variants, the shape of a record, the surface of a module. Where a fact already has a definition, delete the copy in the code and read the definition instead. Where the code holds a fact nothing declares, write the declaration and make the code derive it, because a fact that lives only in behaviour cannot be checked without running the behaviour.",
boundary:
"A definition declares what and never how. A schema that carries a validation routine, or a manifest that carries a build step, has crossed into code and gained a second implementation of something the code already does; the split holds only while each side stays on its own side.",
cause: "Restating a definition in code is cheaper than reading it at the moment of writing, and the cost only arrives when one of the two copies changes and the other keeps the old truth.",
decision:
"Read the definition at the site rather than restate it there, even where reading costs more on the day.",
failureMode:
"A registry lists twelve variants, a switch in the composer handles eleven, and the twelfth exists everywhere except where it is dispatched, because the switch was a second declaration nobody knew was one.",
kind: "lesson",
principle:
"Definitions own what, code owns how, and a fact with two declarations and no derivation between them is a fracture.",
problem:
"Code that restates a definition looks complete on the day it is written and becomes a second truth the day the definition moves.",
validation:
"Take any fact the system carries and count the places it is stated. One, plus derivations, is the target. Two statements with no edge between them will disagree, and the only question is when someone notices.",
},
{ caption: "derivation or restatement", kind: "mermaid", text: DECLARATION_DIAGRAM },
],
title: "One declaration, derived everywhere",
},
{
blocks: [
{
kind: "text",
text: "This is single source of truth stated for a whole system rather than for a database. Declarative configuration states what is wanted and leaves the how to whatever reads it. Manifest-based design puts what a module is for into data beside the module, so its surface is derived rather than described. Metadata-driven design lets the description drive the behaviour.",
},
{
kind: "text",
text: "That is where code as data starts to pay. A definition that is data can be inspected, transformed, validated and generated from. A definition that is code can only be run. The registry pattern with auto-discovery applies the same idea to variants: the registry declares that variants exist, the tree holds one file per variant, and a glob-resolvable tree lets the code find them without a list that must be edited when one is added.",
},
{
kind: "text",
text: "Convention over configuration is the honest complement. A convention is a definition too, written once as a rule the reader derives from rather than a value the reader looks up.",
},
],
title: "The patterns that keep the split",
},
{
blocks: [
{
kind: "text",
text: "The graph model is what makes the split checkable. A declaration is a node, a derivation is an edge, and a fact with two nodes and no edge between them is the dual write in the graph's own terms. The model as a type is small, and its smallness is the point.",
},
{
kind: "text",
text: "A component carries an identity, a concern and a layer. A relation carries its two ends and a kind from a closed vocabulary, so a new relation kind is a vocabulary edit rather than a new field. The schema carries the invariants and one evaluation over the graph, and schema validation is that evaluation run over every record that claims the shape. Health is one derivation: evaluate the schema against the system's description of itself, and an empty finding set is a healthy system.",
},
{ code: SYSTEM_SHAPE, kind: "code", language: TYPESCRIPT_LANGUAGE, title: "the model typed" },
],
title: "The model as a type",
},
{
blocks: [
{
kind: "text",
text: "A definition may say a record has a name and a kind from a closed set. It may not say how the kind is checked, because checking is behaviour. A manifest may say a module publishes three entry points. It may not build them. Either crossing produces the same defect, a truth held in two places with no edge between them.",
},
],
title: "Where the line sits",
},
],
title: "Definitions own what, code owns how",
};
export const GRAPH_SECTIONS: readonly Section[] = [GRAPH_SECTION, DEFINITION_SECTION];