Skip to content

feat: directed arrows, infrastructure nodes, Container Diagram tab#1

Merged
abdullahbodur merged 10 commits intomainfrom
feature/directed-arrows-infra-container-diagram
Mar 21, 2026
Merged

feat: directed arrows, infrastructure nodes, Container Diagram tab#1
abdullahbodur merged 10 commits intomainfrom
feature/directed-arrows-infra-container-diagram

Conversation

@abdullahbodur
Copy link
Copy Markdown
Owner

@abdullahbodur abdullahbodur commented Mar 21, 2026

Summary

  • Directed arrows on all graph edges — markerEnd: "arrow" added to service-flow, data-flow, and container-diagram edges so dependency direction is unambiguous
  • Infrastructure nodes — services can declare databases, queues, caches, and external APIs in archmap.yml via an infrastructure: block; scanner resolves ref: fields from same-repo YAML files (and cross-repo on GitHub scan)
  • Container Diagram tab — new 4th tab with a C4-style view: domain boundary boxes group services, infra nodes rendered with distinct shapes (cylinder for DB, parallel lines for queue), shared infra IDs deduplicated across services
  • Remove emojis from all source files

Changes

Types (all 3 type files)

  • Added InfraType, InfraNode; RawInfraDecl in scanner for partial YAML declarations
  • Extended ViewEdge with markerEnd? and type?
  • Extended ViewNode with parentId?, extent?, style? for React Flow subflows
  • Added infrastructure? to AnalyzedService and containerDiagram to GraphData.views

Scanner

  • parseRepoConfig parses infrastructure[] from archmap.yml
  • Ref resolution: resolveInfraRefsLocal (same-repo ./path.yml) and resolveInfraRefsGithub (same-repo + cross-repo repo/path.yml?ref=tag); inline fields override ref fields

Graph Builder

  • markerEnd: "arrow" on all edges in service-flow and data-flow
  • New container-diagram.ts view: domain group nodes, service child nodes, infra nodes with type-mapped node types, directed smoothstep edges

Web UI

  • 4 new node components: DatabaseNode, QueueNode, CacheNode, ExternalNode
  • GraphTabs: Container tab added as 4th tab
  • GraphView: registers new node types, container diagram empty state

Sample services

  • order-service and inventory-service archmap.yml updated with infrastructure: blocks
  • New infra/postgres-archmap.yml and infra/kafka-archmap.yml ref files under each service (shared id: kafka deduplicates to one Kafka node)

Docs

  • configuration.md: infrastructure field reference, InfraNode table, ref file format, Container Diagram view description
  • use-cases.md: Example commerce org with order-service + inventory-service, local run instructions

Test plan

  • pnpm build — no TypeScript errors
  • pnpm test — 49 tests pass
  • Local scan produces services[].infrastructure with resolved name/technology/description from ref files
  • Service Flow tab — edges show arrowheads
  • Container tab — commerce boundary box, Orders DB + Inventory DB (cyan), single Kafka node (purple), edges labeled correctly
  • Infra nodes display ref path in footer

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

abdullahbodur and others added 10 commits March 21, 2026 18:57
- Add directed arrowheads (markerEnd: "arrow") to all edges in
  service-flow and data-flow views so dependency direction is
  unambiguous at a glance

- Add InfraNode / InfraType types across scanner, graph-builder, and
  web; RawInfraDecl in scanner for partial YAML declarations that rely
  on ref files for name/type

- Scanner parses infrastructure[] from archmap.yml; resolves same-repo
  refs (./path.yml) in both local and GitHub scan modes, and cross-repo
  refs (repo/path.yml?ref=tag) in GitHub scan mode; inline fields take
  priority over ref file fields

- New container-diagram view builder: domain boundary group nodes,
  service child nodes, infra nodes (databaseNode / queueNode /
  cacheNode / externalNode) placed below groups; edges labeled
  "persists to", "publishes to", "subscribes to", "caches via", "calls";
  shared infra id deduped to one node

- Four new React Flow node components: DatabaseNode (cyan cylinder),
  QueueNode (purple parallel-lines), CacheNode (amber), ExternalNode
  (gray dashed); all show ref path in node footer

- Add Container tab as 4th tab in the web UI

- Add infrastructure declarations to order-service and inventory-service
  archmap.yml with ref files under infra/; shared kafka id deduplicates
  to one Kafka node in the container diagram

- Remove all emojis from source files

- Update docs/configuration.md with infrastructure field reference,
  InfraNode fields table, ref file format, and Container Diagram view
  description; update docs/use-cases.md with example commerce org

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Link to the docktail/archmap repo in both configuration.md and
use-cases.md so readers can clone the repo and run the commerce
domain example (Order Service + Inventory Service) immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
graph-builder:
- service-flow: assert markerEnd: "arrow" on dependsOn and kafka edges
- data-flow: new test file — empty graph, deduplication, edge creation,
  markerEnd: "arrow", self-loop prevention, multi-type edge label
- container-diagram: new test file — service nodes, group nodes with
  parentId/extent, infra type mapping (database/queue/cache/external),
  deduplication by id, edge creation, edge labels per infra type
  (persists to / publishes to / subscribes to / uses / caches via / calls),
  markerEnd and smoothstep on all edges

scanner:
- extract parseRepoConfig and resolveInfraRefsLocal into src/config.ts
  so they can be imported without pulling in the full scanner entrypoint
- config.test.ts: parseRepoConfig covers all fields, skip flag, domain
  alias, type validation, infrastructure inline/ref/missing cases;
  resolveInfraRefsLocal covers ref resolution, inline-wins merge,
  missing ref fallback, cross-repo ref skip, multi-node resolution
- exclude src/__tests__ from tsc compilation to prevent dist/ clash

97 tests total passing (was 49)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React Flow only auto-generates SVG arrow marker definitions when
markerEnd is passed as { type: "arrow" }. Passing the plain string
"arrow" is treated as a raw SVG marker ID reference that does not exist,
so no arrowhead was rendered.

Convert in GraphView.tsx before handing edges to useEdgesState, keeping
the graph-builder output framework-agnostic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ut spacing

All node components (ServiceNode, DataTypeNode, FunctionNode, DatabaseNode,
QueueNode, CacheNode, ExternalNode) now expose 8 named handles (source and
target at top/bottom/left/right) so edges can connect to any side.

GraphView computes sourceHandle/targetHandle based on relative node center
positions — horizontal-dominant pairs use left/right; vertical-dominant pairs
use top/bottom — ensuring lines attach to the correct dock point rather than
defaulting to the bottom handle.

Dagre autolayout now reads node.measured dimensions (falls back to 260×160) and
uses nodesep=80, ranksep=200 to prevent nodes from overlapping or crowding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…verlap resolution

Layout (layout.ts):
- Default spacing is now 64px (nodesep=64, ranksep=128), configurable via
  the new `spacing` param on applyDagreLayout
- resolveOverlaps() runs after dagre to iteratively push apart any top-level
  nodes whose bounding boxes still overlap (up to 100 iterations, 16px padding)
- Child nodes (parentId set) are excluded from overlap resolution

Edge routing (GraphView.tsx):
- Extracted getAbsoluteCenter and routeEdges as pure module-level functions
- routeEdges tracks per-node per-handle occupancy; when multiple edges share the
  same node side, subsequent ones are distributed to the next least-used dock
  rather than stacking on a single point

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply a three-layer z-index stack so connection lines are never hidden under
domain boundary boxes in the container diagram:
  group nodes  → zIndex 0 (background layer)
  edges        → zIndex 1 (always visible above group boxes)
  service/infra nodes → zIndex 2 (rendered on top of edge lines)

This is a no-op for views without group nodes (serviceFlow, dataFlow,
functionFlow) since the relative order of nodes and edges is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes fixed:

1. Layout was only applied on button click — edges were rendered with the
   graph-builder grid positions where straight bezier lines could cut through
   intermediate nodes. Dagre layout is now applied automatically whenever the
   view data changes (useMemo keyed on view/viewType/selectedServiceId), so
   nodes are always in proper channel-separated positions before the graph
   is first shown.

2. Edges defaulted to bezier curves which draw direct diagonals across the
   canvas. Changed default to smoothstep (orthogonal right-angle routing)
   so edges travel horizontally and vertically through the clear channels
   between ranked nodes rather than crossing node boxes.

Edge routing is now done AFTER dagre layout so sourceHandle/targetHandle
assignments reflect final node positions. handleAutoLayout re-routes edges
from the original view data so markerEnd is never double-converted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React Flow renders all nodes in an HTML layer that sits above the edge SVG
layer by default — no explicit zIndex needed to achieve boxes > lines > frame.

Removed the edge zIndex:1 elevation (which caused lines to float on top of
group container borders) and the per-node zIndex grouping. All boxes now
render above all edges via React Flow's natural stacking order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the generic applyDagreLayout with getLayoutedElements, a Dagre
wrapper that understands the microservice diagram's node categories:

  Service / group nodes   → placed as LR anchors (natural leftmost rank)
  Database / Cache nodes  → forced two ranks right via weight=3 minlen=2
                            on service→db edges, so databases always appear
                            clearly to the right of their owning service
  Queue / Bus nodes       → excluded from Dagre entirely; manually placed
                            as a horizontal strip below-centre of all other
                            nodes with spacing*2 vertical gap
  Child nodes             → reattached unchanged (relative to parent group)

Edge deduplication prevents Dagre from seeing repeated source→target pairs
(child nodes resolved to their parent group before adding to the graph).

Overlap resolution runs after all positioning to fix any residual collisions.

applyDagreLayout is kept as a thin backward-compat wrapper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@abdullahbodur abdullahbodur merged commit 9d132a0 into main Mar 21, 2026
2 checks passed
@abdullahbodur abdullahbodur deleted the feature/directed-arrows-infra-container-diagram branch March 21, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant