Appearance
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:
- A live runtime topology that reflects what is actually running (not hand-drawn guesses)
- Auto-generated diagrams (ERD, import graph) that cannot go stale because they are regenerated from source on every push
- Per-table and per-type reference docs generated from Prisma schema and TypeScript
- 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-generatoradded as a Prisma generator block; emits a Mermaid entity-relationship diagram of all 40 models onprisma generate. Output committed todocs/internal/architecture/generated/erd.md. - Module dependency graph:
dependency-cruiservalidates import boundaries on every lint run and emits a Mermaid import graph for the monorepo. Output committed todocs/internal/architecture/generated/deps.md. - OpenAPI spec (phase 3):
@fastify/swaggerwithfastify-type-provider-zodto 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 variablesscripts/check-schema-drift.ts: fails CI when a Prisma model has no matching section indata-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 ​
| Phase | Scope |
|---|---|
| 1 — Quick wins | ERD generator, dependency-cruiser lint gate, docs structure scaffold |
| 2 — Publish | MkDocs site + CI build-arch-docs job + SWA deploy |
| 3 — API spec | @fastify/swagger + TypeDoc + Redoc in site |
| 4 — Drift gates | hardcoded-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/swaggeradoption (phase 3) touchesapp.tsand 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
.gitattributeslinguist-generated=trueand a dedicatedgenerated/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.