# Aktuelle Systemarchitektur ## Status und unterstützter Pfad Der unterstützte Editor ist Circuit-First: `Project → DistributionBoard → CircuitList → CircuitSection → Circuit → CircuitDeviceRow` Ein Stromkreis ist nicht dasselbe wie eine Gerätezeile. BMK, Schutz- und Kabeldaten gehören zum Stromkreis; Last-, Raum- und Kategoriedaten gehören zur Gerätezeile. Die frühere Consumer-Oberfläche und ihre API sind entfernt. Die Tabelle `consumers` sowie Mappings und Reports bleiben ausschließlich erhalten, damit ältere Datenbanken über den expliziten Upgrade-Befehl migriert und geprüft werden können. ## Laufzeit ```text Browser :3001 │ ▼ Next.js App Router ── /api/* Rewrite ──▶ Express API :3000 │ ▼ Repository / Transaktion │ ▼ data/leistungsbilanz.db (SQLite) ``` Im Docker-Entwicklungssetup laufen Frontend und API in getrennten Containern. Das Frontend leitet `/api/*` über `API_INTERNAL_URL` an die API weiter. Die Datenbank liegt über einen Host-Mount außerhalb des Containers. ## Wichtige Einstiegspunkte - `src/app/projects/page.tsx` – Projektliste und globale Gerätebibliothek - `src/app/projects/[projectId]/page.tsx` – Projektstammdaten, Verteilungen, Räume und Projektgeräte - `src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx` – unterstützte Editorroute - `src/frontend/components/circuit-tree-editor.tsx` – Editorzustand, Befehlsausführung, Drag-and-drop und der schrittweise Historien-Cutover - `src/frontend/components/circuit-grid-*.ts` – reine Grid-Projektion, Zellbesitz, Einfügen und Sicherheitsregeln - `src/frontend/utils/api.ts` – typisierte Frontend-API-Aufrufe - `src/server/index.ts` und `src/server/routes/` – API-Komposition - `src/domain/services/` – fachliche Command- und Synchronisierungsregeln - `src/db/repositories/` – Abfragen, Persistenzmapper und Transaktionsadapter - `src/db/schema/` und `src/db/migrations/` – SQLite-Schema und Migrationen ## Daten- und Befehlsfluss 1. Das Grid projiziert den geladenen Circuit-Tree in sichtbare Zeilen. 2. Eine Benutzeraktion wird im Frontend validiert und als API-Befehl gesendet. 3. Controller validieren Requestdaten mit Zod. 4. Domain-Services prüfen fachliche Regeln wie BMK-Eindeutigkeit, Abschnittszuordnung und Reserveverhalten. 5. Repositories schreiben Daten. Kritische Mehrfachschreibvorgänge besitzen explizite SQLite-Transaktionsadapter mit Commit-/Rollback-Integrationstests. 6. Das Frontend lädt den Circuit-Tree neu und stellt Auswahl beziehungsweise Viewport soweit möglich wieder her. Der Editor besitzt keinen sitzungslokalen Undo-/Redo-Stapel mehr. Beim initialen Laden und nach jedem Tree-Reload liest er den persistenten History-Status und gleicht dessen Revision mit `currentRevision` des Trees ab. Das Datenmodell besitzt einen projektbezogenen Revisionszähler sowie getrennte Revision-/Change-Set- Tabellen. Ein getestetes Repository kann diese Historienmetadaten optimistisch und atomar fortschreiben. Vorwärts- und Rückwärtskommandos besitzen einen versionierten, JSON-sicheren Umschlag; Typ und Payload können dadurch nach einem Neustart verlustfrei rekonstruiert werden. Konkrete Fachkommandos und bestehende Fachoperationen sind aber noch nicht allgemein an diese Grenze angeschlossen. Für Circuit- und Gerätezeilen-Feldänderungen existieren interne Command-Stores, die Fachänderung, automatisch erzeugtes inverses Kommando und Revision gemeinsam committen beziehungsweise zurückrollen können. Gerätezeilen-Kommandos bewahren dabei auch lokale ProjectDevice-Overrides und prüfen Projektzugehörigkeit von Verknüpfungen und Räumen. Projektweite, persistente Undo-/Redo-Stacks verwalten die zulässige LIFO-Reihenfolge und verwerfen den Redo-Zweig bei einem neuen Benutzerkommando. Ihr Status ist über `GET /api/projects/:projectId/history` lesbar. Ein zentraler Dispatcher führt die unterstützten Typen `circuit.update` und `circuit-device-row.update` über öffentliche Command-, Undo- und Redo-Endpunkte aus. Zusätzlich sind `circuit-device-row.insert` und `circuit-device-row.delete` als atomare Strukturkommandos vorhanden. Beim Löschen wird die vollständige Zeile im inversen Kommando gesichert, sodass Undo dieselbe UUID und alle Fachwerte wiederherstellt. `circuit.insert` und `circuit.delete` behandeln einen Stromkreis mit null, einer oder mehreren Gerätezeilen als vollständigen Block. Undo bewahrt dabei sämtliche Circuit-/Row-UUIDs und ändert keine Betriebsmittelkennzeichen. Bestehende Circuit- und Gerätezeilen-Zelländerungen sowie eigenständiges Einfügen und Löschen verwendet das Grid bereits über die öffentliche Command-Grenze. Neue Circuits und Gerätezeilen erhalten ihre stabile UUID vor dem Command; Löschen und Undo bewahren diese Identität. Der Tree liefert dazu `currentRevision`; Undo/Redo für diese Aktionen läuft über die projektweite Serverhistorie. Direkte Feld-PATCH-, Struktur-POST-, Move-, Circuit- und Gerätezeilen-DELETE-Endpunkte sind entfernt. Gerätezeilen-Moves, Stromkreis-Reorders und die explizite Neunummerierung im Grid verwenden die persistenten Kommandos. Die Toolbar leitet ihre Undo-/Redo-Verfügbarkeit direkt aus den serverseitigen Stack-Tiefen ab, sodass ein Reload die Bedienbarkeit nicht unterbricht. `circuit-device-row.move` verschiebt oder sortiert eine oder mehrere Zeilen zwischen vorhandenen Stromkreisen derselben Liste. Erwartete und neue Stromkreis-/Sortierpositionen machen Forward und Inverse deterministisch; Reservewerte aller beteiligten Stromkreise werden atomar neu abgeleitet. Das Kompositkommando `circuit-device-row.move-with-new-circuit` bildet auch das Verschieben auf einen freien Platz ab: Ein Zielstromkreis mit stabiler UUID und BMK wird zusammen mit allen Zeilenbewegungen erzeugt. Undo stellt die exakten Quellpositionen wieder her und löscht den erzeugten Stromkreis nur, wenn dessen Felder und vollständiger Zeilenbestand unverändert sind. Der Editor erzeugt die vollständigen Move-Zuweisungen aus dem geladenen Tree, vergibt für neue Ziele vor dem Kommando eine stabile UUID und führt das Toolbar-Undo/Redo über die projektweite Historie aus. `circuit.reorder-section` speichert die erwartete und neue Sortierposition jedes Stromkreises eines vollständigen Abschnitts. Forward, Undo und Redo ändern ausschließlich `sortOrder`; Stromkreisblöcke, Gerätezeilen und BMKs bleiben unverändert. `circuit.renumber-section` bildet die getrennte, ausdrücklich ausgelöste Neunummerierung ab. Es speichert alle erwarteten und neuen BMKs des Abschnitts, löst Tauschkollisionen über temporäre Werte und ändert weder Sortierung noch Gerätezeilen. Die Bereichsaktion erzeugt die vollständigen erwarteten und neuen BMKs aus Präfix und aktueller Stromkreisreihenfolge und verwendet das persistente Kommando für Toolbar-Undo/Redo. Die früheren direkten Renumber- und Identifier-Restore-Endpunkte sowie ihr separater Transaktionsadapter sind entfernt. Drag-and-drop verwendet `circuit.reorder-section`. Die explizite Übernahme einer sortierten Ansicht verwendet `circuit.reorder-sections`, damit alle betroffenen Bereiche in einer Transaktion und als ein Undo-Schritt gespeichert werden. Direkte Reorder-Endpunkte existieren nicht mehr. `project-device.sync-rows` persistiert Synchronisierung, Trennen und erneutes Verknüpfen als atomaren Mehrzeilen-Command. Jede betroffene Zeile enthält den vollständigen erwarteten und neuen Stand aller synchronisierbaren Felder, einschließlich ProjectDevice-Verknüpfung und lokaler Override-Metadaten. Damit werden stille Überschreibungen veralteter Zeilen verhindert und Undo/Redo stellt exakt die vorherigen lokalen Werte wieder her. Die Synchronisieren- und Trennen-Endpunkte sowie die Projektseite verwenden diesen Command mit optimistischer Revisionsprüfung. Das dortige Rückgängig löst die persistente projektweite Historie aus; separate direkte Restore-/Reconnect-Schreibwege existieren nicht mehr. `project-device.update` versioniert Änderungen an den kanonischen Projektgerätefeldern unabhängig davon. Der Store erzeugt die Inverse aus dem gespeicherten Gerät und schreibt Geräteänderung, Revision und Historienstapel atomar. Verknüpfte Stromkreiszeilen werden dabei bewusst nicht automatisch synchronisiert. Der ProjectDevice-`PUT`-Endpunkt und die Projektseite verwenden diesen Command mit optimistischer Revisionsprüfung. `project-device.insert` und `project-device.delete` versionieren außerdem den Lebenszyklus eines Projektgeräts mit stabiler UUID. Beim Löschen speichert das inverse Insert den vollständigen Gerätestand sowie vollständige, nach dem Löschen erwartete Snapshots aller zuvor verknüpften Gerätezeilen. Undo setzt die Links nur zurück, wenn diese Zeilen weiterhin zum Projekt gehören, unverknüpft und vollständig unverändert sind. Gerät, Linkänderungen, Revision und Historienstapel teilen dieselbe Transaktion. Create, Import aus der globalen Gerätebibliothek und Delete laufen über dieselbe Command-Grenze; ihre Antworten liefern Gerät und aktualisierten Historienstand an die Projektseite zurück. ## Projektgeräte `ProjectDevice` verwendet ausschließlich die kanonischen Circuit-First-Felder: `phaseType`, `powerPerUnit`, `simultaneityFactor`, `cosPhi`, `remark` sowie optionale technische und kategorisierende Felder. Beim Einfügen entsteht eine verknüpfte `CircuitDeviceRow`. Der Anzeigename wird kopiert, aber nicht still synchronisiert. Spätere Änderungen am Projektgerät werden als Diff angezeigt und nur für ausdrücklich gewählte Felder und Zeilen übernommen. Die globale Gerätebibliothek ist ein einfacher, datenbankweiter Vorlagenbestand. Kopieren in ein Projekt erzeugt ein eigenständiges Projektgerät. ## Persistenz und Migration - SQLite ist die aktuell unterstützte Datenbank. - Fremdschlüssel werden für jeden Datenbankkontext aktiviert. - `npm run db:migrate` wendet Drizzle-Migrationen an. - `npm run db:verify:circuit-schema` prüft erforderliche und entfernte Spalten. - `npm run db:backup` erzeugt ein konsistentes und verifiziertes Online-Backup. - Angewendete Migrationen werden niemals nachträglich verändert. - `db:migrate:legacy-consumers` ist Upgrade-Werkzeug, kein Anwendungspfad. PostgreSQL ist bewusst nicht implementiert. Die Domainregeln und Transaktionsgrenzen sollen portabel bleiben; Schema und Betriebsmodell benötigen bei einem späteren Wechsel trotzdem einen eigenen PostgreSQL-Adapter. ## Noch nicht unterstützt - vollständiges persistentes Undo/Redo, sichtbare Historie nach Reload und benannte Projektstände - Mehrbenutzerbetrieb und Konfliktauflösung - Revit-/CSV-/IFCGUID-Round-trip - vollständige elektrische Dimensionierung - Produktionsdeployment Details: [Bekannte Einschränkungen](circuit-list-editor-known-limitations.md) und [Zukunftsarchitektur](project-history-and-external-model-architecture.md).