MÓDULO 3.3

🌐 Camada de domínio e escala visual: business domain knowledge + graph layout scaling

Anote o grafo com hierarquia domain → flow → step para responder "o que esse código faz", e use ELK + containers + lazy two-stage layout para escalar até milhares de nós sem virar espaguete.

6
Tópicos
60
Minutos
Avançado
Nível
Arquitetura
Tipo
Dashboard alternando entre visão estrutural e camada de domain knowledge: nós ganham anotações de negócio como Order Management, Auth, Payment Flow sobrepostas ao grafo de arquivos.
A camada de domínio em ação: o mesmo grafo estrutural é re-anotado com nós de domain, flow e step + edges implements. É o que este módulo ensina a gerar com /understand-domain e domain-graph.json.
1

🏛️ Hierarquia domain → flow → step

Edges de import só repetem o que a IDE já mostra. Knowledge graph estrutural não responde "o que esse código faz" — só "como ele se conecta". A camada de domínio resolve isso anotando 3 níveis hierárquicos sobre o grafo.

🧠 Três níveis

  • 1.
    Business Domain (topo) — cluster: "Order Management", "Logistics", "Warehouse"
  • 2.
    Business Flow (meio) — processo: "Create Order", "Process Refund"
  • 3.
    Business Step (folha) — passo: "Validate input", "Check inventory", "Persist order"

📄 Domain node (exemplo)

{
  id: "domain:order-management",
  type: "domain",
  name: "Order Management",
  summary: "Handles the complete order lifecycle...",
  tags: ["e-commerce", "core-business"],
  complexity: "complex",
  domainMeta: {
    entities: ["Order", "LineItem", "OrderStatus"],
    businessRules: ["Orders require inventory check before confirmation"],
    crossDomainInteractions: ["Triggers Logistics on confirmed"]
  }
}
3 node types
domain · flow · step
4 edge types
contains · flow_step · cross · implements
Ordering
weight em flow_step
Drill-down
implements → nó estrutural
2

📂 domain-graph.json: arquivo separado, schema compartilhado

Approach C do design: arquivos separados (knowledge-graph.json + domain-graph.json) usando o mesmo tipo KnowledgeGraph estendido. Dashboard detecta ambos e oferece um toggle.

✓ Vantagens

  • /understand-domain roda standalone (leve)
  • Schema compartilhado → search, validation, filtros já funcionam
  • Sem risco de poluir o grafo estrutural
  • Cada arquivo independentemente válido

✗ Alternativas rejeitadas

  • Arquivo único: poluição, dificil iterar
  • Schema totalmente novo: quebra search e validação existentes
  • Anotar nodes estruturais com domain field: mistura camadas
  • Backend separado: complexidade open-source desnecessária

📄 Toggle no dashboard

.understand-anything/
├── knowledge-graph.json   ← grafo estrutural (sempre presente)
└── domain-graph.json      ← camada de domínio (opcional)

Dashboard detecta ambos:
  - Se só estrutural: mostra view padrão
  - Se ambos: toggle "Domain ↔ Structural" no canto sup. dir.
  - Domain view = default quando disponível

💡 Dica prática

Para projetos legados, comece só com o domain-graph. Você anota a hierarquia de negócio sem rodar o pipeline pesado de estrutural. Vincula implements aos arquivos depois.

3

📐 Por que substituir dagre por ELK

Com 50+ nós no mesmo rank, o applyDagreLayout TB enfileira tudo numa linha de milhares de pixels. Resultado: nós shrinkam, labels somem, edges viram espaguete. ELK distribui em 2D com algoritmos modernos.

📊 Scope das views afetadas

  • Overview (layer clusters): dagre → ELK. Sem novo grouping (layers já agrupam).
  • DomainGraphView: dagre → ELK com domain como parent de flow/step.
  • Layer-detail: dagre → ELK + folder/community containers + edge aggregation + lazy 2-stage.
  • KnowledgeGraphView: permanece em applyForceLayout — fora do escopo.

📄 Pipeline de layout

existing graph (immutable)
    ↓
deriveContainers(nodes, edges)      // §2 folder + community fallback
    ↓
buildCompoundGraph()                // agrega edges inter-container
    ↓
runStage1Layout(containers)         // ELK só nos containers
    ↓ [render: containers laid, children unrendered]
    ↓
runStage2Layout(container)          // ELK em filhos sob demanda
    ↓
React Flow render (parentId)
Lib
ELK.js (browser)
Algorithms
layered · mrtree · force
Schema
Inalterado
Target
≤100/layer hoje, 1000+ futuro
4

📦 Containers: folder strategy + community fallback

Sem containers, 100+ nós soltos não têm âncora visual. A layer-detail ganha containers derivados de pasta (default) com fallback para community detection quando a estrutura de pasta é rasa demais.

1

Folder strategy (default)

Quando estrutura de pasta é informativa

Agrupa nodes pelo primeiro segmento significativo do path. src/auth/* → container "auth". src/api/* → container "api".

2

Community fallback

Quando todos os arquivos estão na mesma pasta

Roda community detection (Louvain ou similar) nos edges do layer. Forma containers sintéticos com label gerado.

3

Edge aggregation

Cross-container default, intra-container surface on demand

Por padrão, edges entre containers são agregadas (uma linha grossa em vez de N). Click no container expande os edges individuais.

4

Single depth (v1)

Sem nesting multi-nível

v1 explicitamente NÃO suporta containers dentro de containers. Mantém o React Flow parentId simples e o layout previsível.

💡 Dica prática

Folder strategy quase sempre ganha — projetos bem organizados já carregam semântica na pasta. Community fallback é seguro como Plan B mas pode gerar labels genéricos ("cluster-1") que confundem.

Grafo em duas camadas (estrutural + domínio) e estratégia de escala visual Diagrama mostrando uma camada estrutural de nós e edges, com uma camada de domain knowledge sobreposta com badges; embaixo, comparação entre 100 nós com layout direto e 5000 nós com clustering aplicado. Duas camadas sobre o mesmo grafo camada estrutural · knowledge-graph.json login.ts session.ts jwt.ts routes.ts order.ts payment.ts nós = arquivos · edges = imports/calls + camada de domínio · domain-graph.json 🔐 Auth 🔐 Auth flow 🧾 Order Management domain · flow · step · edges implements (tracejado) Layout que escala: 100 nós (direto) vs 5.000 nós (clustering) 100 nós · ELK direto cada nó renderizado individualmente ✓ first paint imediato · sem clustering necessário 5.000 nós · containers folder/community + lazy two-stage stage 1: só containers · stage 2: filhos sob demanda auth/ ~120 nós (lazy) api/ ~340 nós (lazy) store/ ~890 nós (lazy) ui/ ~1.2k infrastructure/ ~780 nós tests/ ~1.5k scripts/ ~170 nós ✓ containers visíveis · drill-down no clique · cross-edges agregadas
Estrutural + domínio + clustering: a camada de domain knowledge (badges roxos) é sobreposta ao grafo estrutural via edges implements. Para escalar até milhares de nós, o layer-detail troca renderização direta por containers folder/community e lazy two-stage layout (stage 1 = só containers, stage 2 = filhos sob demanda).
5

🐢 Lazy two-stage layout

Layoutar 1000+ nós de uma vez trava o browser. Two-stage entrega first paint rápido e adia trabalho até o usuário pedir.

⚙️ Estágios

  • Stage 1 — Containers only: ELK roda só nos containers e edges agregadas. Usuário já vê estrutura.
  • Stage 2 — Children on demand: ELK roda nos filhos de UM container quando o usuário clica, dá zoom > 1.0, busca, focus ou tour hit.
  • Size memory: tamanhos de containers calculados em Stage 1 são memorizados para Stage 2 não realocar.

📊 Invariantes preservadas

  • 1. Layout pure & memoized: só recomputa quando topologia / persona / diff / focus / nodeTypeFilters mudam
  • 2. Visual state é overlay O(n) separado: selection, search highlight, tour highlight, hover NÃO triggam relayout

📄 Cache por container

// useLayerDetailGraph.ts (existente, hook split)
const stage1 = useStage1Layout(containers, aggEdges)
const stage2Cache = useRef(new Map<string, LayoutResult>())

function triggerStage2(containerId) {
  if (stage2Cache.current.has(containerId)) return
  runELK(container.children).then(result => {
    stage2Cache.current.set(containerId, result)
    forceUpdate()
  })
}
First paint
< 500ms p/ 1000 nós
Trigger Stage 2
click · zoom · search · tour
Cache
Por container · invalidate em topology
Visual
Overlay puro
6

⚠️ GraphIssue: falhas com mensagem, nunca tela em branco

Falhas de layout (ELK timeout, container vazio, edge inválida) são modeladas como GraphIssue — o mesmo tipo já usado pelo schema validator. Renderizam no banner do dashboard com mensagem actionable.

🚨 Anti-pattern: tela branca

Sem GraphIssue, ELK falha e o dashboard mostra nada. Usuário não sabe se o pipeline gerou grafo, se o schema falhou, ou se o layout deu pau. Mensagem clara > silêncio sempre.

✓ FAZER

  • try/catch ao redor de cada ELK call
  • Mensagem com container affected + sugestão
  • Fallback para grafo flat se container falhar
  • Banner usa o mesmo componente do schema validator

✗ EVITAR

  • console.error e seguir como se nada → tela em branco
  • Telemetria remota (Sentry) — não-goal open-source
  • Mensagens genéricas tipo "Erro" sem contexto
  • Crash do React por uncaught promise rejection

📄 GraphIssue type (compartilhado)

type GraphIssue = {
  severity: "warning" | "error"
  source: "schema" | "layout" | "ingest"
  message: string                  // human-readable
  context?: { containerId?: string; nodeId?: string }
  suggestion?: string              // actionable next step
}

// Exemplo:
{
  severity: "warning",
  source: "layout",
  message: "Container 'auth' has 0 visible children after filtering",
  context: { containerId: "auth" },
  suggestion: "Disable node type filter or re-run /understand"
}

💡 Dica prática

Sem telemetria remota é decisão consciente — projeto open-source não envia dados de usuário por padrão. Usuários reportam via GitHub issue com o conteúdo do banner. Banner com suggestion = issue de qualidade.

🎓 Resumo do módulo (e da trilha)

domain → flow → step — responde "o que faz", não só "como conecta"
domain-graph.json separado — schema compartilhado, toggle no dashboard
Dagre → ELK — em todas views structural-style
Folder containers + community fallback — âncora visual em 100+ nós
Lazy two-stage layout — first paint rápido, children on demand
GraphIssue para falhas — banner actionable, nunca tela branca

Você concluiu a Trilha 3 — Avançado 🚀

Agora você sabe estender, escalar e visualizar o Understand Anything em produção. Próximo passo: contribuir no repositório no GitHub.