Files
leistungsbilanz-ts/docs/current-architecture.md
T

36 KiB
Raw Blame History

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, ihre API, Tabellen und Upgrade-Werkzeuge sind entfernt. Neue Funktionen bauen ausschließlich auf dem Circuit-First-Modell auf.

Laufzeit

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/server/composition/ Verdrahtung fachlicher Services mit konkreten SQLite-Repositories
  • 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. Eine direkt getestete Persistence-Funktion schreibt diese Historienmetadaten innerhalb der zentralen Command-Transaktion optimistisch und atomar fort. Vorwärts- und Rückwärtskommandos besitzen einen versionierten, JSON-sicheren Umschlag; Typ und Payload können dadurch nach einem Neustart verlustfrei rekonstruiert werden. Alle aktuell unterstützten Circuit-, Gerätezeilen-, Projektgeräte-, Projektstruktur- und Projekteinstellungsänderungen verwenden typisierte Command-Stores, die Fachänderung, automatisch erzeugtes inverses Kommando, Revision und Historienstapel gemeinsam committen beziehungsweise zurückrollen. 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. Die unveränderliche, absteigend paginierte Revisions-Timeline ist ohne Befehls-Payloads über GET /api/projects/:projectId/history/revisions lesbar. Ein zentraler Dispatcher führt die nachfolgend beschriebenen typisierten Kommandos über öffentliche Command-, Undo- und Redo-Endpunkte aus. Alle unterstützten Runtime-Project-Command-Stores einschließlich der vollständigen Snapshot-Wiederherstellung verwenden dabei project-command-transaction.persistence.ts als gemeinsame äußere Transaktionsgrenze. Sie führt Fachänderung, Revisions-Append und History-Transition in fester Reihenfolge innerhalb derselben SQLite-Transaktion aus. Für CircuitDeviceRow-Feldänderungen übernimmt eine typisierte Variante den während der Fachänderung um Override-Metadaten ergänzten Forward-Command. Die fachlichen Validierungs-, Forward- und Inversenregeln bleiben im jeweiligen Store. Die Low-Level-Funktion appendProjectRevision bleibt ein internes Detail dieser Persistenzgrenze und wird direkt mit einer realen SQLite-Transaktion getestet. Ein eigenständiges Runtime-Revisions-Repository existiert nicht. ProjectDeviceSyncService und CircuitNumberingService kennen nur schmale, fachlich benannte Reader-Interfaces. Ihre SQLite-Repositories werden ausschließlich in src/server/composition/ erzeugt und injiziert. Auch die allgemeinen Projekt-, Geräte-, Raum-, Geschoss- und Circuit- Repositories verlangen einen expliziten AppDatabase-Kontext. Ihre Anwendungsinstanzen werden zentral in src/server/composition/application-repositories.ts erzeugt; Controller importieren weder den globalen SQLite-Client noch konkrete Repository-Klassen. circuit-device-row.insert und circuit-device-row.delete sind atomare Strukturkommandos. 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.

Benannte logische Projektstände werden in project_snapshots getrennt von Datenbank-Backups gespeichert. POST /api/projects/:projectId/snapshots erzeugt bei passender erwarteter Revision transaktional einen vollständigen, schema-versionierten Projektzustand mit SHA-256-Prüfwert. Enthalten sind Projekteinstellungen, Verteiler, Stromkreislisten, Bereiche, Stromkreise und Gerätezeilen sowie Projektgeräte, Geschosse und Räume. Globale Geräte sind nicht Teil des Projekt-Snapshots. Create/List verändern weder Projektrevision noch Undo-/Redo-Stapel. kind unterscheidet benannte und automatische Stände. Die zentrale Revisionspersistenz erzeugt nach jeweils 25 weiteren Projektänderungen transaktional einen automatischen Stand. Pro Projekt bleiben die neuesten 12 automatischen Stände erhalten; ältere automatische Stände werden in derselben Transaktion entfernt. Benannte Stände und die unveränderliche Revisionshistorie sind von dieser Aufbewahrung ausdrücklich ausgeschlossen. POST /api/projects/:projectId/snapshots/:snapshotId/restore prüft Payload, Prüfsumme, erwartete Revision und den unmittelbar zuvor gelesenen Projektzustand. Der Restore ersetzt alle unterstützten Projektdaten in einer Transaktion und schreibt dabei eine neue Revision mit Quelle restore sowie ein vollständiges inverses Kommando. Undo und Redo können deshalb auch einen Restore nach einem Neustart exakt zurücknehmen oder wiederholen. Die Projektseite bindet diese APIs in einem einklappbaren Bereich „Versionen und Sicherungspunkte“ ein. Dort können Benutzer Sicherungspunkte benennen, jeden aufgeführten benannten oder automatischen Stand nach expliziter Bestätigung wiederherstellen und die paginierte Revisions-Timeline mit deutschen Quellen- und Änderungsbezeichnungen lesen. Die Snapshot-Liste ordnet jede positive sourceRevision serverseitig ihren unveränderlichen Revisionsmetadaten zu. Dadurch zeigt auch ein älterer automatischer Stand die auslösende Änderung, ohne eine zweite frei formulierte Beschreibung zu speichern oder von den zuletzt paginiert geladenen Timeline-Einträgen abzuhängen. Revision null wird als Projektstart dargestellt. Projektweites Rückgängig/Wiederholen bleibt im Kopf dieses Bereichs auch im eingeklappten Zustand erreichbar. Die Verfügbarkeit stammt direkt aus den persistierten Server-Stacks; nach einer Historienaktion oder einem Restore lädt die Seite sämtliche Projektdaten neu. 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. Die Projektgerätepalette belegt keine permanente Layoutspalte mehr. Sie wird über die Editor-Toolbar als überlagernder Drawer geöffnet, während das Stromkreis-Grid standardmäßig die gesamte verfügbare Breite nutzt. Auswahl, Schnelleinfügen und die vorhandenen Drag-and-drop-Payloads bleiben im Drawer unverändert verfügbar; während eines aktiven Projektgeräte-Drags kann er nicht geschlossen werden. Der Drawer liegt am rechten Fensterrand und verdeckt damit nicht die führenden BMK- und Anzeigenamenspalten. Das Grid zeigt Geräte- und Stromkreisleistungen in einer gemeinsamen Spalte Gesamtsumme: Gerätezeilen verwenden rowTotalPower, Stromkreis-Sammelzeilen circuitTotalPower. Zahlen werden ausschließlich für die Anzeige deutsch und begrenzt formatiert; gespeicherte Werte und Bearbeitungsentwürfe behalten ihre volle Genauigkeit. Jeder Abschnitt liefert und zeigt seine aufsummierte Stromkreisleistung. Die Stromkreisliste zeigt außerdem die ungefilterte Gesamtleistung des Verteilers, den am Verteiler gespeicherten Gleichzeitigkeitsfaktor und die daraus abgeleitete Gesamtleistung unter Berücksichtigung dieses Faktors. Sortierung und Filter verändern diese fachlichen Summen nicht. Reihenfolge und Sichtbarkeit der Grid-Spalten sind reine UI-Präferenzen. Sie werden im Browser unter einem projektspezifischen Schlüssel gespeichert und deshalb beim Wechsel zwischen Verteilern desselben Projekts wiederverwendet, ohne Projektrevisionen oder fachliche Snapshots zu erzeugen. 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. Rückgängig/Wiederholen erfolgt dort einheitlich über die persistente projektweite Historie; der frühere sitzungslokale Spezial-Undo und 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. project.update-settings versioniert Projektname, interne und externe Projektnummer, Bauherr, Beschreibung, die Kennzeichnung als öffentliches Gebäude, beide Standardspannungen sowie die im Projekt freigeschalteten Verteiler-Netzarten als eine atomare Änderung. Die Gebäudekennzeichnung ist der spätere Eingang für die Auswahl halogenfreier Kabel und Leitungen. Der Systemkatalog besteht aus AV, SV, EV, USV, MSR und SiBe; mindestens eine Netzart muss aktiv bleiben und eine bereits von einer Verteilung verwendete Netzart kann nicht deaktiviert werden. Die Projektseite bearbeitet diese Angaben in einem beschrifteten Einstellungsmodal statt in einer permanenten Formularkarte. Der Store leitet das inverse Kommando aus dem gespeicherten Projekt ab und schreibt Werte, Revision und Historienstapel gemeinsam. PUT /api/projects/:projectId verlangt deshalb expectedRevision, liefert Projekt plus aktualisierten Historienstand und besitzt keinen separaten direkten Settings-Schreibweg mehr. Kommando- und Snapshot-Payloads müssen dem aktuellen Baseline-Schema entsprechen. Die Projektseite zeigt Verteilungen, Etagen und Räume als kompakte Bestandsübersichten; ihre versionierten Erstellwege öffnen beschriftete Modals statt dauerhafter Eingabezeilen. Projektgeräte werden als durchsuchbare, fünfspaltige Übersicht dargestellt. Manuelle Anlage, Übernahme aus der globalen Bibliothek und vollständige Bearbeitung erfolgen in einem gemeinsamen Modal. Die anschließende Vorschau verknüpfter Stromkreiszeilen bleibt davon getrennt, damit Änderungen weiterhin niemals still synchronisiert werden. GET /api/projects/:projectId/export verpackt denselben vollständigen Projektzustand in ein portables, format- und schema-versioniertes JSON-Dokument mit SHA-256-Prüfsumme. POST /api/projects/:projectId/import prüft Format, Snapshot-Relationen und Prüfsumme vor jedem Schreibzugriff. Der Modus replace läuft über project.restore-state und ist dadurch eine atomare, dauerhaft rückgängig machbare Projektrevision. Der Modus duplicate ordnet Projekt-, Struktur-, Gruppen-, Verteilerkomponenten-, Raum-, Stromkreis- und Gerätezeilen-UUIDs sowie Schutzgeräte-Referenzen vollständig neu zu und legt die Kopie mit Revision 0 in einer Transaktion an. Fachliche Verknüpfungen innerhalb des unterstützten Laufzeitmodells bleiben erhalten. Die Projektübersicht verwendet dafür den separaten Collection-Endpunkt POST /api/projects/import, der ausschließlich eine neue Kopie anlegt und deshalb weder eine bestehende Projekt-ID noch expectedRevision annimmt. Das Ersetzen eines Projekts bleibt auf dessen Einstellungsmodal und den projektgebundenen Endpunkt beschränkt. distribution-board.insert versioniert die Anlage einer Verteilung mit Command-Schema 1 als vollständigen Block aus Verteilung, Stromkreisliste, den drei Gruppen Beleuchtung 1, 1-phasig 1, 3-phasig 1, Hauptschalter -Q0 und Überspannungsableiter -FA. Alle UUIDs entstehen vor dem Command und bleiben über Undo/Redo stabil. distribution-board.delete ist die persistierte Inverse und entfernt nur den vollständig unveränderten, weiterhin stromkreislosen Block. Controller und Projektseite übergeben die erwartete Projektrevision; der frühere direkte Controller-Schreibweg ist entfernt. Verteilungen besitzen eine optionale Etagenreferenz sowie eine Netzart aus dem Projektkatalog. Anlage und nachträgliche Bearbeitung prüfen die Projektzugehörigkeit der Etage und die Freigabe der Netzart in den Projekteinstellungen. Bereits befüllte Verteilungen werden über distribution-board.insert-subtree und distribution-board.delete-subtree als vollständiger Unterbaum kopiert beziehungsweise gelöscht. Der Snapshot umfasst Verteilung, Stromkreisliste, Gruppen, Stromkreise, Gerätezeilen, Verteilerkomponenten und alle zugehörigen Schutzgeräte. Beim Kopieren werden sämtliche besitzenden UUIDs neu vergeben, während fachliche Verknüpfungen zu Projektgeräten und Räumen erhalten bleiben. Beide Aktionen sind atomare Projektrevisionen und bleiben nach einem Neustart über Undo/Redo umkehrbar. Die Projektseite bietet sie im Einstellungsmodal der jeweiligen Verteilung an; Löschen verlangt dort eine ausdrückliche Bestätigung. distribution-board-component.insert und distribution-board-component.delete sowie distribution-board-component.update versionieren Anlage, Entfernung, Bearbeitung und Sortierung veränderlicher Gruppen-Schutzgeräte und zusätzlicher Verteilergeräte. Die vollständigen Snapshots enthalten stabile Komponenten-UUIDs und, für Gruppen-Schutzgeräte, die getrennte 1:1-Schutzgerätekonfiguration. Updates tauschen einen exakt erwarteten gegen einen vollständigen Zielzustand aus; ID, Eigentum, Rolle und Platzierungszone bleiben dabei unverändert. Projekt-, Listen- und Gruppenzugehörigkeit sowie veraltete Zustände werden innerhalb derselben Transaktion geprüft. Die festen Kopfkomponenten Hauptschalter und Überspannungsableiter sind von diesen allgemeinen Commands ausgeschlossen. Veränderliche Gruppen- und Fußkomponenten werden im Editor über eigene Modale und diese Commands verwaltet. Leere Stromkreisgruppen werden über circuit-group.insert, circuit-group.update und circuit-group.delete verwaltet. Ihr vollständiger Snapshot koppelt Kategorie, positive Gruppennummer und daraus abgeleiteten BMK-Präfix; allgemeine Updates ändern ausschließlich den Anzeigenamen. Löschen verlangt einen exakt unveränderten Zustand ohne Stromkreise und ohne Gruppenkomponenten. Befüllte Gruppen verwenden getrennt davon den vollständigen circuit-group.delete-subtree-Command mit ausdrücklicher Warnung. circuit-group.reorder sortiert Gruppen als vollständige Stromkreislisten- Zuordnung. Jede vorhandene Gruppe muss mit erwarteter und neuer Position enthalten sein. Der Command verändert ausschließlich sortOrder; Nummern, Präfixe und Stromkreis-BMKs bleiben stabil. Die gesamte Sortierung bildet eine Revision und einen Undo/Redo-Schritt. src/domain/services/circuit-group-renumbering.ts plant explizite Gruppennummerierungen deterministisch. Der reine Plan erhält Stromkreis-Endnummern, leitet Gruppenpräfixe und optionale .0-Komponenten-BMKs neu ab und prüft Nummerntausch sowie Kollisionen mit unveränderten Gruppen. circuit-group.renumber führt diesen Plan über die gemeinsame Projekt-Command-Transaktion aus. Vor dem Schreiben müssen alle betroffenen Gruppen, Stromkreise und Gruppenkomponenten exakt dem erwarteten Zustand entsprechen. Nummerntausch verwendet kollisionsfreie temporäre Präfixe und BMKs über beide BMK-Tabellen; anschließend werden alle Zielwerte finalisiert. Der vollständige inverse Plan ermöglicht dauerhaftes Undo/Redo. src/domain/services/circuit-group-move-planning.ts plant den Wechsel eines vollständigen Stromkreises zwischen zwei verschiedenen Gruppen derselben Kategorie. Die Ziel-BMK verwendet die höchste vorhandene Stromkreis-Endnummer der Zielgruppe plus eins; Lücken werden nicht gefüllt. Quell-/Zielgruppe, Quell-/Zielposition und beide BMKs werden festgeschrieben, damit eine spätere Wiederholung nichts neu berechnet. circuit.move-group führt den geplanten Wechsel als eine Projekt-Revision aus. Nur sectionId, equipmentIdentifier und sortOrder des Stromkreises ändern sich. Gerätezeilen und das getrennte 1:1-Schutzgerät bleiben über die stabile Stromkreis-UUID verbunden. Der inverse Wechsel speichert Quellgruppe, BMK und Position vollständig für dauerhaftes Undo/Redo. CircuitGroupSubtreeSnapshot ist der kanonische Vertrag für destruktive Gruppenoperationen. Er umfasst Gruppe, optionale Gruppenkomponenten samt 1:1-Schutzdaten, vollständige Stromkreise samt 1:1-Schutzdaten und sämtliche Gerätezeilen einschließlich Link- und Override-Metadaten. Seine Warnzusammenfassung liefert Vorsicherung/FI sowie Stromkreis- und Gerätezeilenanzahl. circuit-group.delete-subtree nimmt den aktuellen Unterbaum unmittelbar vor dem Löschen erneut auf und verlangt exakte Übereinstimmung mit dem bestätigten Snapshot. circuit-group.restore-subtree schreibt ihn in FK-sicherer Reihenfolge mit denselben UUIDs zurück. Beide Richtungen laufen als vollständige inverse Projekt-Commands; späte Fehler hinterlassen weder Teillöschungen noch Teilwiederherstellungen. Der Circuit-Tree liefert zusätzlich headerComponents, gruppenbezogene components und footerComponents. Abschnitte enthalten optionale Gruppenkategorie und -nummer; Stromkreise und Gruppenkomponenten enthalten ihre getrennte Schutzgerätekonfiguration. Die 1:1-Schutzgerätetabellen sind die einzige unterstützte Quelle dieser Daten. Separate aktive Read-Repositories lesen Komponenten- und beide Schutzgerätetabellen; Schreibzugriffe bleiben ausschließlich in Commands. src/frontend/utils/circuit-structure-projection.ts projiziert diesen Tree rein und deterministisch in die drei Zonen Kopf, vollständige Gruppen und Fuß. Innerhalb einer Gruppe folgen auf die Überschrift optionale Schutzkomponenten, vollständige Stromkreisblöcke und der freie Einfügeplatz. Leere Gruppen bleiben sichtbar; Komponenten werden stabil nach sortOrder und ID sortiert. circuit-grid-projection.ts normalisiert diese Struktur zusammen mit den bisherigen kompakten und mehrzeiligen Stromkreisblöcken für den Editor. Kopf-, Gruppen- und Fußkomponenten erscheinen als Zeilen über die volle Tabellenbreite und nehmen nicht an Zellauswahl oder Tastatur-Einfügen teil. Hauptschalter und Überspannungsableiter bleiben feste, schreibgeschützte Kopfkomponenten. Optionale Gruppenvorsicherungen, Gruppen-FIs und zusätzliche Verteilergeräte werden über beschriftete Modale angelegt, bearbeitet und nach Bestätigung entfernt. Der Frontend-API-Adapter serialisiert dafür die vorhandenen distribution-board-component.*-Commands; die verbindliche geräteabhängige Validierung und atomare Revision bleiben serverseitig. Neue Stromkreisgruppen erhalten im Editor die höchste vorhandene Gruppennummer ihrer Kategorie plus eins und ein daraus abgeleitetes Präfix. Das Bearbeiten ändert ausschließlich den Anzeigenamen. Nur vollständig leere Gruppen können über circuit-group.delete entfernt werden; für befüllte Gruppen bleibt die Aktion bis zum gesonderten Warn- und Unterbaumdialog gesperrt. circuit-protection.update bildet die eigene persistente Schreibgrenze für die neue 1:1-Stromkreisschutztabelle. Der Command vergleicht den vollständigen erwarteten Datensatz, validiert Typ und abhängige Felder, prüft die Projektzugehörigkeit und schreibt Schutzgerät, Revision und Historienübergang atomar. Benutzer können Schutzdaten anlegen oder ändern, aber nicht entfernen; Undo einer erstmaligen Anlage darf den zuvor fehlenden Datensatz exakt wiederherstellen. Der vollständige CircuitSnapshot enthält den zugehörigen protectionDevice-Datensatz, sofern einer vorhanden ist. Neue Stromkreise und durch Geräteverschiebung erzeugte Zielstromkreise verwenden die vereinbarten Kategorie-Standardwerte und schreiben Schutzgerät, Stromkreis sowie Gerätezeilen atomar. Delete/Undo erfasst denselben Datensatz vollständig. Im Editor sind die Schutzspalten deshalb eine schreibgeschützte Projektion der 1:1-Daten; Änderungen erfolgen über ein geräteabhängiges Schutzgeräte-Modal und circuit-protection.update. Gruppen lassen sich im Editor schrittweise nur gegenüber einer benachbarten Gruppe derselben Kategorie verschieben. Der Client sendet dabei immer die vollständige erwartete und gewünschte Sortierreihenfolge über circuit-group.reorder; Gruppennummer, Präfix und Kind-BMK bleiben unverändert. Eine separate, ausdrücklich bestätigte Editoraktion erzeugt dagegen einen vollständigen circuit-group.renumber-Plan für genau eine Kategorie. Sie nummeriert deren Gruppen gemäß aktueller Reihenfolge ab eins und ändert Gruppenpräfixe, optionale Gruppen-Schutz-BMK und Stromkreis-BMK gemeinsam; die Stromkreisnummer hinter dem letzten Punkt bleibt erhalten. Das vorhandene Stromkreis-Drag-Handle kann außerdem genau einen vollständigen Stromkreis in eine andere Gruppe derselben Kategorie verschieben. Der atomare circuit.move-group-Befehl erhält Gerätezeilen und Schutzdaten, setzt die Zielposition und vergibt dort die höchste vorhandene Stromkreisnummer plus eins; Lücken werden nicht automatisch gefüllt. Befüllte Gruppen werden im Editor erst nach einer Warnung mit Anzahl der enthaltenen Stromkreise, Gerätezeilen und Gruppenschutzgeräte gelöscht. Der Client sendet dafür den vollständigen aktuellen Unterbaum an circuit-group.delete-subtree; der Server vergleicht ihn innerhalb derselben Transaktion mit dem Datenbankstand. Undo stellt Gruppe, Schutzgeräte, Stromkreise, Gerätereihen und Verknüpfungs-/Override-Metadaten vollständig wieder her. distribution-board.update versioniert Etage, Netzart und den verteilerweiten Gleichzeitigkeitsfaktor gemeinsam und stellt alle Werte über dauerhaftes Undo/Redo wieder her. Der Faktor liegt zwischen 0 und 1 und ist für bestehende sowie neu angelegte Verteilungen standardmäßig 1. Snapshot-Schema 4 enthält zusätzlich den vollständigen externen Modellzustand aus Quelle, Batches, Raum-Mappings und Objekten. Es enthält außerdem die projektweite Revit-CSV-Konfiguration, die Gebäudekennzeichnung sowie den Gleichzeitigkeitsfaktor, Gruppenkategorie und -nummer, Verteilerkomponenten sowie die getrennten 1:1-Schutzgerätedaten. Capture, benannte und automatische Snapshots, Wiederherstellung, Undo/Redo und beide JSON-Importmodi verwenden denselben vollständigen Zustand. Die unterstützte Version 3 wird mit leerem externem Modellzustand und Version 2 zusätzlich mit leerer externer Konfiguration hochgestuft. Die unterstützte Baseline-Version 1 wird zusätzlich mit isPublicBuilding = false hochgestuft; Formate vor dieser Baseline werden nicht eingelesen. Persistente Insert-, Update- und Delete-Commands versionieren Anlage, Bearbeitung und Löschung von Geschossen und Räumen mit stabilen UUIDs und exakten Vorher-/Nachher-Snapshots. Ein Geschoss wird nur entfernt, solange ihm weder ein Raum noch eine Verteilung zugeordnet wurde. Ein Raum wird nur entfernt, solange keine CircuitDeviceRow auf ihn verweist. Alle Schreibendpunkte verlangen expectedRevision und liefern den aktualisierten Historienstand.

Projektgeräte

Die Projektgerätekategorie lighting, single_phase oder three_phase ist die fachliche Klassifikation und Zielgruppe. phaseType ist keine separate Benutzereingabe, sondern wird daraus für Spannung, Verknüpfung und spätere Dimensionierung abgeleitet.

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 richtet mit Baseline-Migration 0000 und den folgenden additiven Migrationen das aktuelle Schema ein.
  • npm run db:verify:circuit-schema prüft erforderliche und entfernte Spalten.
  • npm run db:backup erzeugt ein konsistentes und verifiziertes Online-Backup.
  • npm run typecheck:scripts prüft die TypeScript-Wartungsskripte, ohne Code zu erzeugen.
  • Die Baseline ersetzt alle vorigen Entwicklungsmigrationen. Datenbanken, Snapshots und persistierte Commands aus Vor-Baseline-Ständen werden bewusst nicht unterstützt. Ab der ersten veröffentlichten Version sind angewendete Migrationen unveränderlich und Änderungen erfolgen additiv.
  • Allgemeine Circuit-, Gerätezeilen-, CircuitList- und DistributionBoard- Repositories stellen im Anwendungspfad nur noch benötigte Leseabfragen bereit. Fachliche Schreibvorgänge liegen in den typisierten Command-Repositories.
  • Die vollständige Verteilungs-Testfixture liegt unter tests/support/distribution-board-fixture.ts und ist kein exportierter Produktions-Schreibweg.
  • Die Baseline bildet die relationale Grundlage für geschützte Stromkreisgruppen und Verteilerkomponenten. Separate 1:1-Tabellen halten Stromkreis- und Komponenten-Schutzgeräte. Die früheren flachen Stromkreis-Schutzfelder sind aus der Baseline entfernt. Ein triggergeführtes Register erzwingt bereits eine normalisierte, stromkreislistenweite BMK-Eindeutigkeit über Stromkreise und Verteilerkomponenten. Snapshot- und Transfer-Integration verwenden aktuell Snapshot-Schema 4. Persistente Insert/Delete/Update-Commands für veränderliche Verteilerkomponenten, Gruppen einschließlich befüllter Unterbäume sowie vollständige Gruppensortierung sind integriert. Der Editor zeigt die geschützte Struktur an und bearbeitet veränderliche Gruppen- und Fußkomponenten über dedizierte Command-Modale. Gruppenanlage, -umbenennung, -sortierung, explizite Neunummerierung, Same-Category-Stromkreiswechsel, geschütztes Unterbaumlöschen und Stromkreisschutz sind integriert.

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.

Revit-CSV-Grundlage

Der reine Adapter unter src/external-model/csv validiert die projektweite Transport- und Mappingkonfiguration und parst UTF-8-CSV verlustarm in eine klassifizierte Zellmatrix. Er erkennt Metadaten, Header, Passthrough-, Objekt- und Verdachtszeilen, prüft IFCGUIDs und serialisiert unveränderte Revit-Exporte einschließlich BOM, CRLF und Quotierung bytegleich zurück. Der lokale Befehl npm run revit:verify-reference prüft die bereitgestellte Referenzdatei.

external_csv_configurations enthält höchstens eine versionierte Konfiguration je Projekt. external-csv-configuration.update schreibt Anlage, Änderung oder Entfernung zusammen mit Revision und persistentem Undo/Redo. Snapshot-Schema 4, Restore und portabler Projekttransfer enthalten diesen Zustand; beim Duplizieren werden Konfigurations-UUID und Projektlink remapped.

Migration 0003 ergänzt die relationale Grundlage für genau eine implizite revit_csv-Quelle je Projekt, geordnete Importbatches, normalisierte Quellraummappings und über (sourceId, ifcGuid) eindeutige externe Objekte. Batches halten Konfigurationssnapshot, Originalbytes und die klassifizierte Zellmatrix zusammen. Migration 0004 ergänzt die monotone projektweite Konfigurationsversion am Batch, getrennt von der Schema-Version des Konfigurationsformats. Quellwerte, lokale Planungswerte und Override-Metadaten der Objekte sind getrennt; optionale Links auf Raum, Verteilung, ProjectDevice und CircuitDeviceRow bleiben nullable. ExternalModelStateRepository liest den vollständigen Zustand deterministisch und unterscheidet unbekannte von noch nicht importierten Projekten. Der letzte bestätigte Batch wird aus der Batchreihenfolge abgeleitet statt redundant an der Quelle gespeichert. Snapshot/Transfer v4 erfasst Originalbytes, Matrix und alle internen Links, stellt sie in Fremdschlüsselreihenfolge wieder her und remappt beim Projektduplikat jede interne UUID; IFCGUID und Quelltransport bleiben unverändert. external-import.apply-initial ist die atomare Schreibgrenze für einen bestätigten Erstimport. Vor dem Insert prüft sie den aktuellen Konfigurationsstand, SHA-256 und Base64-Kanonizität, parst die Originalbytes erneut, vergleicht Matrix, vollständige IFCGUID-Menge und Quellwerte und validiert alle Raum-, Verteiler- und ProjectDevice-Links gegen das Projekt. Nicht explizit als Override markierte Planungswerte müssen dem regelbasierten Vorschlag entsprechen. Der Command erzeugt und verknüpft ausdrücklich keine CircuitDeviceRow; Undo entfernt den vollständigen unveränderten Importzustand, Redo verwendet dieselben UUIDs und Bytes. Ein aufrufender Import-Endpunkt und der mehrstufige Wizard sind noch nicht implementiert; bloße Vorschau erzeugt weiterhin keine dieser Zeilen. GET und PUT /api/projects/:projectId/external-csv/configuration lesen oder ändern die Konfiguration; der PUT plant Identität und nächsten Konfigurationsstand serverseitig und verwendet den typisierten Command. POST /api/projects/:projectId/external-csv/preview nimmt Dateiname und Base64-kodierte CSV bis 18 MB entgegen. Der zustandsfreie Service liefert Hash, Dialekt, Header, Klassifikationszahlen, Verdachtszeilen und gemappte Objektquellwerte. Er schreibt weder Entwurf noch Projektdaten. POST /api/projects/:projectId/external-csv/initial-import/plan verwendet denselben begrenzten Transport, setzt eine gespeicherte Konfiguration und einen noch leeren externen Projektzustand voraus und bleibt ebenfalls zustandsfrei. Die Antwort gruppiert Quellräume, schlägt bei genau einer identischen Raumnummer den vorhandenen Raum vor, gruppiert exakte Familie-und-Typ-Werte, liefert regelbasierte Objektplanungswerte und Konfliktzahlen und enthält die vorhandenen Ebenen, Räume, Verteilungen und ProjectDevices als Auswahlkataloge. Unbekannte Familien, ungültige Leistungen/Mengen und Objekte ohne Raum bleiben sichtbare Warnungen; es wird keine Entscheidung automatisch bestätigt. Die Projektseite öffnet über „Revit-CSV“ ein deutsches Modal für Dialekt, Spaltenzuordnung, zusätzliche Quellfelder und exakte Familie-und-Typ-Regeln. Nach dem revisionierten Speichern kann eine lokale CSV gewählt und über den zustandsfreien Endpunkt geprüft werden. Die Oberfläche zeigt Kennzahlen und nur die ersten 25 erkannten Objekte; die vollständigen Objekte bleiben in der Response verfügbar, ohne alle Zeilen in den DOM einzufügen.

Noch nicht unterstützt

  • Mehrbenutzerbetrieb und Konfliktauflösung
  • bestätigter Revit-Erstimport und vollständiger CSV-/IFCGUID-Round-trip über die vorhandene Transport- und Persistenzgrundlage hinaus
  • vollständige elektrische Dimensionierung
  • Produktionsdeployment

Details: Bekannte Einschränkungen und Zukunftsarchitektur.