Archify Renderer Architecture: How Typed Renderers Share Geometry Utilities
Archify implements a typed-renderer architecture where five independent renderers import pure geometry helpers from a shared module to ensure consistent diagram layout across architecture, workflow, sequence, dataflow, and lifecycle diagrams.
Archify is a JSON-to-diagram rendering engine that transforms intermediate representation (JSON-IR) into self-contained HTML visualizations. The renderer architecture in the tt-a1i/archify repository cleanly separates diagram-type concerns from reusable geometric operations, enabling maintainable and performant code generation.
Overview of the Typed-Renderer Architecture
Archify organizes renderers by diagram type under archify/renderers/<type>/. Each renderer is a standalone ES module responsible for one visualization style:
archify/
└─ renderers/
├─ architecture/
│ └─ render‑architecture.mjs
├─ workflow/
│ └─ render‑workflow.mjs
├─ sequence/
│ └─ render‑sequence.mjs
├─ dataflow/
│ └─ render‑dataflow.mjs
├─ lifecycle/
│ └─ render‑lifecycle.mjs
└─ shared/
├─ geometry.mjs
├─ utils.mjs
└─ validator.mjs
This structure enforces single-responsibility at the directory level while promoting horizontal reuse through the shared/ subdirectory.
The Shared Geometry Module
All five renderers import from archify/renderers/shared/geometry.mjs. This module exports pure, side-effect-free functions for rectangle, segment, and point operations. Because the helpers carry no internal state, renderers can invoke them safely without risk of cross-contamination.
Core Geometry Functions
| Function | Signature | Purpose |
|---|---|---|
rectsOverlap |
(a, b, gap?) => boolean |
Detects axis-aligned rectangle intersection with optional clearance |
segmentIntersectsRect |
(segment, rect, gap?) => boolean |
Determines if a line segment touches or crosses a rectangle |
segmentRectClearance |
(segment, rect) => number | null |
Returns minimum distance between segment and rectangle, or 0 on intersection |
segmentRectIntersectionLength |
(segment, rect) => number |
Computes overlapped length of segment inside rectangle |
collectLabelRouteClearance |
({labels, routedRelations, threshold}) => ClearanceInfo |
Aggregates clearance data for automatic edge-label routing |
These functions enable renderers to measure, test, and adjust layout tables before emitting final SVG or HTML output.
How Renderers Use Shared Geometry Utilities
Import Pattern
Every renderer follows the same import convention:
// From any renderer (e.g., workflow)
import {
rectsOverlap,
segmentIntersectsRect,
segmentRectClearance,
} from '../shared/geometry.mjs';
The relative path ../shared/ resolves consistently because all renderers sit at the same directory depth.
Workflow Renderer Example
In archify/renderers/workflow/render-workflow.mjs, collision detection and edge routing rely on shared helpers:
// archify/renderers/workflow/render-workflow.mjs
import { rectsOverlap, segmentRectClearance } from '../shared/geometry.mjs';
function placeStep(step, existingNodes) {
const stepRect = { x: step.x, y: step.y, width: step.w, height: step.h };
for (const node of existingNodes) {
if (rectsOverlap(stepRect, node.rect, 8)) {
// Resolve collision by adjusting stepRect position
}
}
return stepRect;
}
function routeEdge(start, end, obstacles) {
const segment = { start, end };
const clearance = obstacles.reduce((c, obs) => {
const cur = segmentRectClearance(segment, obs.rect);
return cur === null ? c : Math.min(c, cur);
}, Infinity);
// Bend edge if clearance insufficient
return clearance < 4 ? bendEdge(segment) : segment;
}
Architecture Renderer Example
The archify/renderers/architecture/render-architecture.mjs renderer applies the same helpers for component packing:
// archify/renderers/architecture/render-architecture.mjs
import { rectsOverlap, segmentIntersectsRect } from '../shared/geometry.mjs';
export function layoutComponents(components) {
// Build component rectangles from JSON-IR
// Ensure no two components overlap
for (let i = 0; i < components.length; ++i) {
for (let j = i + 1; j < components.length; ++j) {
if (rectsOverlap(components[i].rect, components[j].rect, 6)) {
// Adjust positions to resolve collision
}
}
}
}
Notice that both renderers import overlapping function subsets—each takes only what it needs, and tree-shaking eliminates dead code in production bundles.
Benefits of the Shared Geometry Design
Archify's renderer architecture delivers four operational advantages:
-
Consistency — All renderers apply identical geometric rules. A gap of 8 pixels means the same visual distance in workflow diagrams as in architecture diagrams.
-
Maintainability — Geometry logic centralizes in one file. A bug fix to
rectsOverlappropagates to every diagram type without individual renderer edits. -
Isolation — Renderers remain independent modules. Developers can test, refactor, or replace
render-sequence.mjswithout touchingrender-dataflow.mjsor the shared layer. -
Performance — Pure functions enable aggressive optimization. JavaScript engines can inline or memoize calls to
segmentRectClearance, keeping render times low even for complex diagrams.
Key Source Files
| File | Role |
|---|---|
archify/renderers/shared/geometry.mjs |
Pure geometry helpers used by all five renderers |
archify/renderers/architecture/render-architecture.mjs |
Architecture diagram renderer |
archify/renderers/workflow/render-workflow.mjs |
Workflow diagram renderer |
archify/renderers/sequence/render-sequence.mjs |
Sequence diagram renderer |
archify/renderers/dataflow/render-dataflow.mjs |
Dataflow diagram renderer |
archify/renderers/lifecycle/render-lifecycle.mjs |
Lifecycle diagram renderer |
archify/renderers/shared/utils.mjs |
Miscellaneous shared utilities |
archify/renderers/shared/validator.mjs |
JSON schema validation across renderers |
Summary
- Archify uses a typed-renderer architecture with five specialized renderers under
archify/renderers/<type>/ - All renderers import pure geometry functions from
archify/renderers/shared/geometry.mjs - Shared helpers include
rectsOverlap,segmentRectClearance, andcollectLabelRouteClearancefor collision detection and edge routing - The pure-function design guarantees consistency, maintainability, isolation, and performance
- Renderers build type-specific layouts then call shared utilities to validate and adjust geometry before output
Frequently Asked Questions
What diagram types does Archify support?
Archify supports five diagram types: architecture, workflow, sequence, dataflow, and lifecycle. Each has a dedicated renderer module under archify/renderers/.
Why use pure functions for geometry operations?
Pure functions eliminate side effects and state leakage between renderers. They also enable JavaScript engine optimizations like inlining and memoization, reducing render time for complex diagrams.
Can I add a new renderer without modifying existing code?
Yes. Create a new directory under archify/renderers/<your-type>/ with a render-<type>.mjs entry point. Import shared utilities from ../shared/geometry.mjs following the existing pattern. The typed-renderer architecture requires no changes to other renderers or the shared module.
How does Archify prevent diagram elements from overlapping?
Renderers call rectsOverlap(a, b, gap?) from the shared geometry module to detect collisions during layout. The optional gap parameter adds configurable padding between elements.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →