From 18bd82e54ac0bb43cf98adad7dfcdf063a550abd Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 3 Jul 2026 10:09:06 +0200 Subject: [PATCH] =?UTF-8?q?Abh=C3=A4ngigkeitsgraph:=20klarere=20Anordnung?= =?UTF-8?q?=20(Abh=C3=A4ngigkeiten=20links,=20Prozess=20Mitte,=20erzeugte?= =?UTF-8?q?=20Assets=20rechts)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Anzeige-Kanten rollenabhängig orientiert: primäre/erzeugte Assets rechts vom Prozess, unterstützende Abhängigkeiten links (Pfeile fließen sauber links→rechts, Label "erzeugt" bzw. "unterstützt") - Analyse-Adjazenz (SPOF, kritischer Pfad, transitive Abhängigkeiten) von der Anzeige entkoppelt und über alle Asset-Bezüge berechnet Co-Authored-By: Claude Opus 4.8 --- src/server/dependency-graph.ts | 64 +++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/server/dependency-graph.ts b/src/server/dependency-graph.ts index 6d07ee2..809eef0 100644 --- a/src/server/dependency-graph.ts +++ b/src/server/dependency-graph.ts @@ -124,9 +124,15 @@ export async function buildDependencyGraph( }); } - // Kanten: Prozess → Asset (Rolle), Asset → Asset (Relation) + // Zwei Sichten: + // - adjacency (Analyse): Abhängigkeitsrichtung (Abhängiger → Abhängigkeit), + // Basis für kritischen Pfad, SPOF, transitive Abhängigkeiten. + // - edges (Anzeige): links = unterstützende Abhängigkeit, Mitte = Prozess, + // rechts = erzeugtes primäres Asset. Pfeile fließen sauber von links nach + // rechts, daher zeigen unterstützende Kanten VON der Abhängigkeit zum + // Nutzer/Prozess. const edges: GraphEdge[] = []; - const adjacency = new Map(); // gerichtete Abhängigkeit source→target + const adjacency = new Map(); const addAdj = (s: string, t: string) => { if (!adjacency.has(s)) adjacency.set(s, []); adjacency.get(s)!.push(t); @@ -136,31 +142,46 @@ export async function buildDependencyGraph( for (const p of processes) { for (const pa of p.processAssets) { - const s = pid(p.id); - const t = aid(pa.assetId); - edges.push({ - id: `${s}->${t}`, - source: s, - target: t, - label: pa.role === "PRIMARY" ? "erzeugt" : "nutzt", - critical: isCritEdge(s, t), - }); - addAdj(s, t); + const proc = pid(p.id); + const asset = aid(pa.assetId); + // Analyse: Prozess ist auf alle zugeordneten Assets angewiesen (SPOF/Pfad) + addAdj(proc, asset); + if (pa.role === "PRIMARY") { + // erzeugtes Asset rechts vom Prozess + edges.push({ + id: `${proc}->${asset}`, + source: proc, + target: asset, + label: "erzeugt", + critical: isCritEdge(proc, asset), + }); + } else { + // unterstützendes Asset links vom Prozess (Kante zeigt zum Prozess) + edges.push({ + id: `${asset}->${proc}`, + source: asset, + target: proc, + label: "unterstützt", + critical: isCritEdge(asset, proc), + }); + } } } const assetIds = new Set(assets.map((a) => a.id)); for (const r of relations) { if (!assetIds.has(r.assetId) || !assetIds.has(r.relatedAssetId)) continue; - const s = aid(r.assetId); - const t = aid(r.relatedAssetId); + // Analyse: asset hängt von relatedAsset ab + addAdj(aid(r.assetId), aid(r.relatedAssetId)); + // Anzeige: Abhängigkeit (relatedAsset) links, Nutzer (asset) rechts + const s = aid(r.relatedAssetId); + const t = aid(r.assetId); edges.push({ id: `${s}->${t}`, source: s, target: t, - label: r.type === "depends_on" ? "hängt ab" : r.type, + label: "unterstützt", critical: isCritEdge(s, t), }); - addAdj(s, t); } // Transitive Abhängigkeiten je kritischem Prozess → SPOF-Zählung je Asset @@ -191,12 +212,13 @@ export async function buildDependencyGraph( } spofs.sort((a, b) => b.count - a.count); - // Längster kritischer Pfad (DFS über kritische Kanten ab kritischen Prozessen) + // Längster kritischer Pfad (Abhängigkeitskette): DFS über die Analyse-Adjazenz, + // beschränkt auf kritische Knoten, ab kritischen Prozessen. const critAdj = new Map(); - for (const e of edges) { - if (!e.critical) continue; - if (!critAdj.has(e.source)) critAdj.set(e.source, []); - critAdj.get(e.source)!.push(e.target); + for (const [s, targets] of adjacency) { + if ((critOf.get(s) ?? 1) < threshold) continue; + const critTargets = targets.filter((t) => (critOf.get(t) ?? 1) >= threshold); + if (critTargets.length) critAdj.set(s, critTargets); } const nameOf = new Map(nodes.map((n) => [n.id, n.name])); let longest: string[] = [];