Skip to content

ADR 0046 — Architecture Documentation and Live Mapping Pipeline ​

Status: Accepted
Date: 2026-06-28
Deciders: Kristopher Turner
Amends: ADR 0005 (Observability Model)
References: ADR 0027 (Design Tooling), ADR 0031 (Code-First Design Workflow)


Context ​

Heritage Community Hub has grown to 40 database tables, 20 API feature routers, and a multi-app monorepo (web, mobile, API, landing). Architecture documentation — a single hand-maintained draw.io file, a partial data-model-design.md covering ~9 of 40 tables, and no API spec — has become a recurring source of bugs: bad cross-module imports, hardcoded URLs that should come from env vars, and Prisma models added without corresponding docs.

The owner needs:

  1. A live runtime topology that reflects what is actually running (not hand-drawn guesses)
  2. Auto-generated diagrams (ERD, import graph) that cannot go stale because they are regenerated from source on every push
  3. Per-table and per-type reference docs generated from Prisma schema and TypeScript
  4. A CI gate that catches bad imports, hardcoded refs, and schema drift before merge

Decision ​

Adopt a four-layer architecture documentation and mapping pipeline, built entirely from open-source generators and Azure-native observability. Zero new monthly cost.

Layer 1 — Live runtime topology ​

Use the Azure Application Insights Application Map, which is already provisioned (infrastructure/modules/observability.bicep) and already receives telemetry from:

  • Heritage API — Node.js Container App (apps/api/src/lib/telemetry.ts)
  • Heritage Web — React SPA (apps/web/src/lib/analytics.ts)

No code change required for this layer. Operationally, APPLICATIONINSIGHTS_CONNECTION_STRING must be set in the Container App environment (tracked under AB#4542).

Layer 2 — Auto-generated diagrams ​

  • ERD: prisma-erd-generator added as a Prisma generator block; emits a Mermaid entity-relationship diagram of all 40 models on prisma generate. Output committed to docs/internal/architecture/generated/erd.md.
  • Module dependency graph: dependency-cruiser validates import boundaries on every lint run and emits a Mermaid import graph for the monorepo. Output committed to docs/internal/architecture/generated/deps.md.
  • OpenAPI spec (phase 3): @fastify/swagger with fastify-type-provider-zod to emit an OpenAPI 3 JSON spec from the existing Zod schemas in the 20 feature routers.

Layer 3 — Per-file / per-table reference docs ​

  • prisma-docs-generator: per-model column, type, and relation reference docs
  • TypeDoc over all packages/ workspaces: exported type and API reference
  • Redoc / mkdocs-render-swagger-plugin: per-endpoint docs from the Layer 2c OpenAPI spec

Layer 4 — Drift detection CI gate ​

  • .dependency-cruiser.cjs: fails CI on cross-app imports, provider-SDK boundary violations, and circular dependencies (ports the existing ESLint zones)
  • scripts/check-hardcoded-refs.ts: fails CI on hardcoded API base URLs or Clerk keys in source that should come from environment variables
  • scripts/check-schema-drift.ts: fails CI when a Prisma model has no matching section in data-model-design.md

Publish ​

MkDocs Material site (docs-site/mkdocs.yml) deployed to a free Azure Static Web App via a new build-arch-docs CI job. Rebuilt on every push to main. The docs_dir points to docs/internal/ so all existing curated prose is included alongside generated artifacts.


Phasing ​

PhaseScope
1 — Quick winsERD generator, dependency-cruiser lint gate, docs structure scaffold
2 — PublishMkDocs site + CI build-arch-docs job + SWA deploy
3 — API spec@fastify/swagger + TypeDoc + Redoc in site
4 — Drift gateshardcoded-ref lint, schema-drift check, promote to blocking

Consequences ​

Positive:

  • ERD and dependency graph are always current — generated from source, not maintained by hand
  • Bad imports, hardcoded refs, and schema drift are caught before merge
  • The Application Map is live; no diagram to update when a new service or dependency is added
  • Zero new monthly cost

Negative / risks:

  • @fastify/swagger adoption (phase 3) touches app.ts and all 20 routers additively; must be kept additive (registering schemas must not change runtime behavior)
  • Generated artifacts committed to the repo increase PR diff noise; mitigated by .gitattributes linguist-generated=true and a dedicated generated/ folder

Alternatives considered ​

Commercial living-architecture SaaS (Structurizr, Swimlane, CodeSee): Evaluated and explicitly deferred. These tools provide richer interactive views but cost $50–$200/month. The free pipeline described here covers the primary use cases. Revisit only if the generated artifacts prove insufficient for the team's needs.

Generating docs to an untracked folder: Rejected. Committed generated artifacts make diffs reviewable in PRs and allow the site to be audited under version control. The linguist-generated attribute suppresses them in GitHub's language stats.

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.