How the Architecture Analyzer Identifies API, Service, Data, UI, and Utility Layers in Understand Anything

The Architecture Analyzer in Understand Anything detects architectural layers by combining directory pattern matching, import graph topology, and semantic heuristics to classify files into logical groups like API, Service, Data, UI, and Utility.

The analyzer lives in understand-anything-plugin/agents/architecture-analyzer.md and operates as a two-phase pipeline that transforms raw file nodes and import edges into a structured layers.json output. This process discovers classic layered architectures automatically without manual configuration.

Two-Phase Layer Detection Workflow

The architecture analyzer processes repository structure through distinct structural and semantic phases to produce deterministic layer assignments.

Phase 1 – Structural Graph Analysis

Directory grouping clusters files by the first directory segment after common prefixes like src/ or lib/. This creates initial boundaries for where code lives within the repository structure.

Node-type grouping categorizes all nodes by their type—file, config, document, service, pipeline, and others. This exposes non-code artifacts that often become separate layers such as Infrastructure, Documentation, or Configuration.

Import adjacency and fan-in/fan-out metrics build an adjacency list for every file, counting how many files it imports (fan-out) and how many import it (fan-in). The analyzer aggregates these metrics per directory group to produce an inter-group import matrix that reveals dependency patterns between clusters.

Pattern matching compares directory names against a curated table of architectural patterns. Known mappings include routes → api, services → service, models → data, components → ui, and utils → utility. File-level glob patterns like *.test.*, *.d.ts, or Dockerfile provide additional classification hints.

Cross-category edges count relationships between non-code node types (such as config → file or service → file) to identify Infrastructure, CI/CD, and Documentation layers.

Density and dependency direction calculations determine intra-group import density (high density indicates a cohesive layer) and establish the dominant import direction between groups, creating a dependency hierarchy where top layers depend on lower layers.

Phase 2 – Semantic Layer Assignment

Using the structural output, the analyzer selects 3–10 layers and assigns every node to exactly one.

Directory groups become layer candidates. If a group matches a pattern label, that label becomes the layer name. Intra-group density exceeding 0.3 forces the group into its own layer because tightly coupled files indicate a distinct architectural boundary.

Inter-group import asymmetry identifies foundational layers (such as utility, types, or data) that are heavily imported by other groups but import few others themselves. Dependency direction orders the layers logically—UI depends on Service, which depends on Data.

Non-code node types generate dedicated layers automatically when present. Dockerfiles create an Infrastructure layer, .github/workflows/ files create CI/CD, README files create Documentation, and *.sql files create Data layers.

Fallback heuristics using file summaries and tags resolve ambiguous cases, particularly in flat src/ directory structures. The final output writes to .understand-anything/intermediate/layers.json with each layer containing an id (e.g., layer:api, layer:service), human-readable name, description, and exact node IDs.

Pattern Matching and Directory Heuristics

The analyzer recognizes architectural layers through deterministic mapping rules rather than machine learning.

Directory-to-Layer Mappings

The system maintains a pattern table that maps conventional directory names to architectural layers:

  • routes/ → API Layer: Exposes HTTP endpoints and request handling
  • services/ → Service Layer: Contains core business logic
  • models/ or db/ → Data Layer: Database access and storage logic
  • components/ or ui/ → UI Layer: User interface elements
  • utils/ or helpers/ → Utility Layer: Shared helper functions

Cohesion Thresholds

When intra-group import density exceeds 0.3, the analyzer automatically elevates that directory group to layer status regardless of naming conventions. This threshold captures tightly coupled modules that represent genuine architectural boundaries even when directory names are unconventional.

Running the Architecture Analyzer

Invoke the analyzer through the generated script at .understand-anything/tmp/ua-arch-analyze.js:


# Create input JSON with file nodes and import edges

cat > .understand-anything/tmp/ua-arch-input.json <<'EOF'
{
  "fileNodes": [
    {"id":"file:src/routes/index.ts","type":"file","name":"index.ts","filePath":"src/routes/index.ts","summary":"API entry point","tags":["api-handler"]},
    {"id":"file:src/services/auth.ts","type":"file","name":"auth.ts","filePath":"src/services/auth.ts","summary":"Auth business logic","tags":["service"]},
    {"id":"file:src/utils/format.ts","type":"file","name":"format.ts","filePath":"src/utils/format.ts","summary":"Utility helpers","tags":["utility"]},
    {"id":"service:Dockerfile","type":"service","name":"Dockerfile","filePath":"Dockerfile","summary":"Container definition","tags":["infrastructure"]}
  ],
  "importEdges": [
    {"source":"file:src/routes/index.ts","target":"file:src/services/auth.ts","type":"imports"},
    {"source":"file:src/services/auth.ts","target":"file:src/utils/format.ts","type":"imports"}
  ],
  "allEdges": [
    {"source":"file:src/routes/index.ts","target":"file:src/services/auth.ts","type":"imports"},
    {"source":"file:src/services/auth.ts","target":"file:src/utils/format.ts","type":"imports"},
    {"source":"service:Dockerfile","target":"file:src/index.ts","type":"deploys"}
  ]
}
EOF

# Execute analysis

node .understand-anything/tmp/ua-arch-analyze.js \
  .understand-anything/tmp/ua-arch-input.json \
  .understand-anything/tmp/ua-arch-results.json

The semantic assignment phase processes these results and writes the final layer definitions to .understand-anything/intermediate/layers.json:

[
  {
    "id": "layer:api",
    "name": "API Layer",
    "description": "Exposes HTTP endpoints and request handling",
    "nodeIds": ["file:src/routes/index.ts"]
  },
  {
    "id": "layer:service",
    "name": "Service Layer",
    "description": "Contains core business-logic services",
    "nodeIds": ["file:src/services/auth.ts"]
  },
  {
    "id": "layer:utility",
    "name": "Utility Layer",
    "description": "Shared helper functions used across the codebase",
    "nodeIds": ["file:src/utils/format.ts"]
  },
  {
    "id": "layer:infrastructure",
    "name": "Infrastructure",
    "description": "Container and deployment configuration",
    "nodeIds": ["service:Dockerfile"]
  }
]

Summary

  • The Architecture Analyzer uses a two-phase pipeline combining structural graph analysis and semantic assignment to identify layers.
  • Directory patterns map conventional folder names like routes/, services/, and utils/ to API, Service, and Utility layers.
  • Import density thresholds (> 0.3) automatically detect cohesive architectural boundaries even with unconventional naming.
  • Fan-in/fan-out metrics reveal dependency hierarchies and foundational layers that are imported frequently but import few others.
  • Non-code artifacts like Dockerfiles and CI/CD configurations generate dedicated Infrastructure, CI/CD, and Documentation layers.
  • Final output persists to .understand-anything/intermediate/layers.json with standardized IDs like layer:api and layer:data.

Frequently Asked Questions

How does the analyzer distinguish between Service and Utility layers?

Service layers contain business logic imported by API routes and UI components, showing high fan-in from presentation layers. Utility layers demonstrate even broader usage patterns—imported by Services, Data access layers, and other Utilities—while maintaining minimal dependencies on other project-specific code. The analyzer detects this through inter-group import asymmetry, where Utilities show high fan-in but low fan-out to domain-specific modules.

What happens when directory names don't match known patterns?

When directories lack conventional names, the analyzer relies on import density calculations. If a directory group's intra-group import density exceeds 0.3, indicating tight internal coupling, it automatically becomes a distinct layer regardless of naming. Fallback heuristics using file summaries and tags provide additional classification signals for flat src/ structures or ambiguous naming conventions.

How does the analyzer handle non-code files like Dockerfiles?

The Architecture Analyzer treats non-code artifacts as first-class nodes. Files like Dockerfile, .github/workflows/*.yml, or README.md receive node types such as service, config, or document. Cross-category edges (e.g., service:Dockerfile → file:src/index.ts with type deploys) trigger the creation of dedicated Infrastructure, CI/CD, or Documentation layers in the final layers.json output.

Can the analyzer detect custom architectural patterns?

Yes. The system combines pattern matching with structural metrics, allowing it to surface custom architectures. Even without matching the built-in directory patterns, groups exhibiting high intra-group density and distinct fan-in/fan-out profiles become layer candidates. The semantic assignment phase uses these structural signatures to generate project-specific layer descriptions, accommodating hexagonal, onion, or domain-driven architectures without explicit configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →