# configuration/strings/layer.strings.ts

> 167 lines of code and 8 definitions.

Tree: Site tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-configuration-strings-layer-strings-ts
Source text: https://banes-lab.com/assets/sources/source.b6716392b8da6dfe209dc7dbe4be76e563d02962359ec98339e25e05c371c317.generated.txt

## Definitions

- `LAYER_SHAPE` (lexical_declaration, line 6)
- `SPINE_DIAGRAM` (lexical_declaration, line 9)
- `CONCERN_DIAGRAM` (lexical_declaration, line 12)
- `DIRECTION_DIAGRAM` (lexical_declaration, line 15)
- `REPAIR_DIAGRAM` (lexical_declaration, line 18)
- `SPINE_SECTION` (lexical_declaration, line 21)
- `DIRECTION_SECTION` (lexical_declaration, line 97)
- `LAYER_SECTIONS` (lexical_declaration, line 175, exported)

## Source

```typescript
import { DIRECTION_SECTION_ICON, SPINE_SECTION_ICON } from "#configuration/icons/architecture.icons";
import { DIRECTION_SECTION_ID, SPINE_SECTION_ID } from "#core/ids/architecture.ids";
import type { Section } from "#types/document.types";
import { TYPESCRIPT_LANGUAGE } from "#configuration/constants/code.constants";

const LAYER_SHAPE =
    'export const SPINE = ["domain", "application", "processing", "runtime", "infrastructure", "operations", "product"] as const;\nexport type Layer = (typeof SPINE)[number];\n\nexport const TIERS = ["engine", "consumer"] as const;\nexport type Tier = (typeof TIERS)[number];\n\nexport interface Classification {\n    readonly concern: Concern;\n    readonly layer: Layer;\n}\n\nexport interface Direction {\n    readonly byPrefix: Readonly<Record<string, Tier>>;\n    readonly overrides: Readonly<Record<string, Tier>>;\n}\n\nexport const tierOf = (path: string, direction: Direction): Tier | null =>\n    direction.overrides[path] ?? direction.byPrefix[prefixOf(path)] ?? null;\n\nexport const crossesUpward = (from: string, to: string, direction: Direction): boolean =>\n    tierOf(from, direction) === "engine" && tierOf(to, direction) === "consumer";';

const SPINE_DIAGRAM =
    'block-beta\n    columns 1\n    domain["domain · what the system is about"]\n    application["application · what it does with that"]\n    processing["processing · how data is transformed"]\n    runtime["runtime · what runs and when"]\n    infrastructure["infrastructure · what everything else stands on"]\n    operations["operations · how it is observed and kept alive"]\n    product["product · what a customer meets"]';

const CONCERN_DIAGRAM =
    'flowchart LR\n    converter["converter"] --> processing["processing"]\n    validator["validator"] --> processing\n    registry["registry"] --> infrastructure["infrastructure"]\n    factory["factory"] --> infrastructure\n    view["view"] --> product["product"]\n    renderer["renderer"] --> product\n    model["model"] --> domain["domain"]\n    policy["policy"] --> domain';

const DIRECTION_DIAGRAM =
    'flowchart TB\n    engine["The engine tier · knows nothing about any consumer"]\n    consumer["The consumer tier · depends on the engine"]\n    consumer -- imports --> engine\n    engine -. never .-> consumer\n    prefix["Classified by container prefix"]\n    override["Two trees a prefix cannot decide · classified per file"]\n    prefix --> engine\n    prefix --> consumer\n    override --> engine\n    override --> consumer';

const REPAIR_DIAGRAM =
    'flowchart LR\n    edge["An edge from the engine toward a consumer"]\n    trick["A trick · lazy import, direct path, re-export, sort order"]\n    move["A move · to the module whose layer and entry point satisfy every dependency"]\n    lies["The graph passes and the structure stays wrong"]\n    holds["The graph and the structure agree"]\n    edge -. tempting .-> trick --> lies\n    edge --> move --> holds';

const SPINE_SECTION: Section = {
    icon: SPINE_SECTION_ICON,
    id: SPINE_SECTION_ID,
    intro: "This section covers the layer spine, the one axis along which systems decompose, running from domain through application, processing, runtime, infrastructure and operations to product, as shown in <cite>the spine</cite> and <cite>concerns to layers</cite>. The spine is a classification axis, and who may import whom is described separately in the direction axis. The practice that parses a tree against the spine is described in placement is a grammar on the methodology page.",
    subsections: [
        {
            blocks: [
                {
                    application:
                        "In practice, every concern in the vocabulary is tagged to one layer of the spine, and a file's layer follows from its concern. A file is classified by what it does, never by the folder it happens to sit in, and a file that fits two concerns equally well is treated as two files rather than as a tie to break. The tagging is held in data a check reads, so a layer is a derivation from the concern and never a fact the developer or the model has to remember.",
                    boundary:
                        "The spine orders kinds of thing and never orders importance. A product-layer file is not lower than a domain-layer file, and a layer is never a folder. Two files in one concern folder sit on the same layer because their concern does, whatever the folder above them is called.",
                    cause: "A layer inferred from a folder name changes when the folder is renamed, and a layer inferred from a file's importance is argued at every review, so only a layer derived from the concern stays true without attention.",
                    decision: "The layer is derived from the concern rather than from the folder or from importance.",
                    failureMode:
                        "A converter sits in a folder named for the feature it serves, the feature is renamed, and every rule that keyed on the folder now sees a file of no layer at all.",
                    kind: "lesson",
                    principle:
                        "For this reason the layer spine classifies what a file is, and a file's layer is read from its concern.",
                    problem:
                        "Layering is usually a diagram, and neither the developer nor the model can say which layer a given file is on, because the layer was never derived from anything the file declares.",
                    validation:
                        "To check this, take a file and derive its layer from its concern tag alone, without opening it. A file whose layer cannot be derived is outside the model, and a file whose derived layer surprises you is misclassified, or is two files.",
                },
                { caption: "the spine", kind: "mermaid", text: SPINE_DIAGRAM },
            ],
            title: "Classification",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "Every layered architecture has to answer what makes a thing belong to a layer, and most answer it by folder. A folder is a rule about placement and says nothing about kind, so the layer of a file is whatever its author believed on the day. Clean architecture and hexagonal architecture answer the direction question well and leave this one to taste.",
                },
                {
                    kind: "text",
                    text: "The spine answers it by kind. Every concern in a closed vocabulary is tagged to one layer, the concern is decided by reading what the file does under one concern per file and the narrowest concern that fits, and the layer is a derivation. Concern-folder correspondence makes the derivation visible in the tree, because the folder names the concern and the concern names the layer, so nothing has to be remembered.",
                },
            ],
            title: "Belonging by kind, never by folder",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "The seven layers map onto how a system decomposes rather than onto how a team is organised. The domain holds what the system is about, such as its models, records, policies and specifications. The application holds what it does with that, such as coordinators, behaviours, intents, selectors and stores. Processing holds transformation, such as converters, normalizers, analyzers, validators and pipelines.",
                },
                {
                    kind: "text",
                    text: "Runtime holds what runs and when, such as entrypoints, lifecycles, timers and pools. Infrastructure holds what everything else stands on, such as registries, factories, adapters, resolvers, constants, schemas and the vocabulary itself. Operations holds observation and upkeep, such as probes, counters and reporters. Product holds what a customer meets, such as views, components, renderers, styles and the strings.",
                },
                {
                    kind: "text",
                    text: "A concern belongs to exactly one layer, and a concern whose layer is contested is two concerns. A file with two concerns is a split, never a tie to break. Layer spine precedence is the one tie-break the canon holds, and it applies only to an irreducible overlap between two tags for one concern. In that case the file classifies to the domain-ward tag, and the rule stays a classification rule, never a dependency rule.",
                },
                { caption: "concerns to layers", kind: "mermaid", text: CONCERN_DIAGRAM },
            ],
            title: "The seven layers",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "Take a converter. It takes one shape and returns another, so it is processing whatever it converts and whichever feature asked for it. Put it in a folder named for the feature and it has a home but no layer, and the next feature that needs the same conversion either reaches across a boundary or copies the file.",
                },
                {
                    kind: "text",
                    text: "Put it under its concern and the layer follows, the second feature finds it where the concern says it is, and a check can hold that nothing in processing reaches into product. Separation of concerns then has a mechanism behind it. Package by feature answers a different question, how a team navigates, and a feature cuts across every layer as a layer cuts across every feature, so only one of the two can be the folder.",
                },
            ],
            title: "A converter, placed twice",
        },
    ],
    title: "The layer spine",
};

const DIRECTION_SECTION: Section = {
    icon: DIRECTION_SECTION_ICON,
    id: DIRECTION_SECTION_ID,
    intro: "This section covers the direction axis, which says who may depend on whom, as shown in <cite>one way</cite>, while the classification described in the layer spine says what kind of thing a file is. The two axes are orthogonal, as typed in <cite>two axes</cite>. The canon's architecture styles are each one picture of the same direction rule, and a wrong-way edge has one repair, shown in <cite>the repair</cite>.",
    subsections: [
        {
            blocks: [
                {
                    application:
                        "In practice, one tier is named the engine and the other the consumer. Each container is classified by prefix, and only the trees a prefix cannot decide are classified per file. Both classifications feed the check, which refuses an import that runs from the engine toward a consumer.",
                    boundary:
                        "The direction rule governs dependencies and says nothing about classification. A file is not on the engine tier because it is generic, and a consumer is not lower because it is specific. The tier is a fact declared about a tree, and the check reads the fact rather than inferring it.",
                    cause: "A dependency rule that has no check behind it is a diagram, and the first import that crosses the wrong way is the one that was convenient that afternoon.",
                    decision:
                        "The tier is declared as data a check reads, rather than inferred from what a file looks like.",
                    failureMode:
                        "A shared module gains one import from a page, the page changes, the module now breaks on every page, and the layering that was supposed to prevent that never had a rule behind it.",
                    kind: "lesson",
                    principle:
                        "For this reason the dependency direction is an orthogonal axis that runs one way, from consumer to engine, and is held by its own check.",
                    problem:
                        "Nothing refuses the import that crosses the wrong way, so the engine slowly learns about its consumers one convenient import at a time.",
                    validation:
                        "To check this, take any import and ask which tier each end is on. An import whose ends cannot be tiered is outside the model, and an import that runs from the engine toward a consumer is a move waiting to happen.",
                },
                { caption: "one way", kind: "mermaid", text: DIRECTION_DIAGRAM },
            ],
            title: "Engine and consumer",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "The direction rule is the dependency inversion principle drawn at the scale of a whole tree, and the canon's architecture styles are each one way of drawing it. Hexagonal architecture, ports and adapters architecture and clean architecture put the thing that knows nothing at the centre and let everything specific depend inward. Layered architecture draws the same arrow downward.",
                },
                {
                    kind: "text",
                    text: "What they share is one direction and one rule. What they differ on is a picture, and the picture is not the mechanism. The mechanism is a tier declared for every file, a check that reads the dependency graph and refuses an edge from the engine toward a consumer, and a repair that is always a move.",
                },
                {
                    kind: "text",
                    text: "Inversion of control and dependency injection are the two techniques the rule pushes you toward. The only way an engine uses something specific without knowing it is to be handed it, and extension points with runtime discovery are how the engine finds the consumers it must not import.",
                },
            ],
            title: "One rule, many pictures",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "Most of a tree classifies by where it sits, because a container is built for one tier and everything under it inherits that. A few kinds of file resist that reading. Copy and type declarations serve whichever side names them, so their location says nothing about their tier, and those are classified one file at a time in data that starts empty.",
                },
                {
                    kind: "text",
                    text: "What a file with no entry resolves to is a decision with a reason, not a default that fell out of the code. A type with no classification resolves to no tier, so the check treats it as unclassified rather than guessing a side. Copy with no classification resolves to the consumer tier, because copy is nearly always specific to one product. The two defaults differ because the cost of a wrong guess differs, and the general rule is that a default is chosen by which mistake is cheaper to discover.",
                },
                { code: LAYER_SHAPE, kind: "code", language: TYPESCRIPT_LANGUAGE, title: "two axes" },
            ],
            title: "Where a tier comes from",
        },
        {
            blocks: [
                {
                    kind: "text",
                    text: "The check reads a dependency graph derived from the tree, never the tree's claims about itself, and it fails closed, so a missing graph is a refusal rather than a pass over nothing. It refuses rather than repairs, because the only repair for a wrong-way import is to move the file.",
                },
                {
                    kind: "text",
                    text: "A file that produces a circular dependency, an upward dependency or a bypass of a declared entry point is in the wrong module. Lazy evaluation of an import, a direct path past the entry point, a re-export across modules and an import-sort trick each make the graph pass while the structure stays wrong. Those tricks preserve concrete coupling and inappropriate intimacy, because the engine still knows a consumer, only through a door no check watches. Encapsulation and information hiding are what the entry point protects.",
                },
                { caption: "the repair", kind: "mermaid", text: REPAIR_DIAGRAM },
            ],
            title: "The repair is a move",
        },
    ],
    title: "The direction axis",
};

export const LAYER_SECTIONS: readonly Section[] = [SPINE_SECTION, DIRECTION_SECTION];
```
