168 lines
9.1 KiB
Markdown
168 lines
9.1 KiB
Markdown
# 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 sitzungslokales Undo/Redo
|
||
- `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.
|
||
|
||
Die React-Historie ist derzeit sitzungslokal. Das Datenmodell besitzt bereits
|
||
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. Das Grid verwendet diese Endpunkte noch nicht; seine
|
||
sichtbare Historie bleibt daher sitzungslokal.
|
||
`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.
|
||
`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.
|
||
`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 bestehenden
|
||
Projektgeräte-Endpunkte verwenden diesen Command bis zur Frontend-Umstellung
|
||
noch nicht.
|
||
`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
|
||
|
||
- persistentes Undo/Redo und Projektversionen
|
||
- 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).
|