Abhängigkeitsgraph: klarere Anordnung (Abhängigkeiten links, Prozess

Mitte, erzeugte Assets rechts)

- 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 <noreply@anthropic.com>
This commit is contained in:
Martin
2026-07-03 10:09:06 +02:00
co-authored by Claude Opus 4.8
parent d9685b65b9
commit 18bd82e54a
+43 -21
View File
@@ -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 edges: GraphEdge[] = [];
const adjacency = new Map<string, string[]>(); // gerichtete Abhängigkeit source→target const adjacency = new Map<string, string[]>();
const addAdj = (s: string, t: string) => { const addAdj = (s: string, t: string) => {
if (!adjacency.has(s)) adjacency.set(s, []); if (!adjacency.has(s)) adjacency.set(s, []);
adjacency.get(s)!.push(t); adjacency.get(s)!.push(t);
@@ -136,31 +142,46 @@ export async function buildDependencyGraph(
for (const p of processes) { for (const p of processes) {
for (const pa of p.processAssets) { for (const pa of p.processAssets) {
const s = pid(p.id); const proc = pid(p.id);
const t = aid(pa.assetId); const asset = aid(pa.assetId);
edges.push({ // Analyse: Prozess ist auf alle zugeordneten Assets angewiesen (SPOF/Pfad)
id: `${s}->${t}`, addAdj(proc, asset);
source: s, if (pa.role === "PRIMARY") {
target: t, // erzeugtes Asset rechts vom Prozess
label: pa.role === "PRIMARY" ? "erzeugt" : "nutzt", edges.push({
critical: isCritEdge(s, t), id: `${proc}->${asset}`,
}); source: proc,
addAdj(s, t); 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)); const assetIds = new Set(assets.map((a) => a.id));
for (const r of relations) { for (const r of relations) {
if (!assetIds.has(r.assetId) || !assetIds.has(r.relatedAssetId)) continue; if (!assetIds.has(r.assetId) || !assetIds.has(r.relatedAssetId)) continue;
const s = aid(r.assetId); // Analyse: asset hängt von relatedAsset ab
const t = aid(r.relatedAssetId); 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({ edges.push({
id: `${s}->${t}`, id: `${s}->${t}`,
source: s, source: s,
target: t, target: t,
label: r.type === "depends_on" ? "hängt ab" : r.type, label: "unterstützt",
critical: isCritEdge(s, t), critical: isCritEdge(s, t),
}); });
addAdj(s, t);
} }
// Transitive Abhängigkeiten je kritischem Prozess → SPOF-Zählung je Asset // 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); 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<string, string[]>(); const critAdj = new Map<string, string[]>();
for (const e of edges) { for (const [s, targets] of adjacency) {
if (!e.critical) continue; if ((critOf.get(s) ?? 1) < threshold) continue;
if (!critAdj.has(e.source)) critAdj.set(e.source, []); const critTargets = targets.filter((t) => (critOf.get(t) ?? 1) >= threshold);
critAdj.get(e.source)!.push(e.target); if (critTargets.length) critAdj.set(s, critTargets);
} }
const nameOf = new Map(nodes.map((n) => [n.id, n.name])); const nameOf = new Map(nodes.map((n) => [n.id, n.name]));
let longest: string[] = []; let longest: string[] = [];