Grundlagen und Sicherheitsvertrag
Zielbild, Schichtentrennung, Kernelgrenzen, Verzeichnisstruktur sowie Routing- und Sicherheitsvertrag.
Referenzstand: 25. Juli 2026
Beispielmodul: myInvoices
Dieses Kapitel ist der verbindliche Golden Path für ein datenbasiertes dbxapp-Modul. Das vollständige Beispiel verwaltet Rechnungen und deren Artikelpositionen. Es verbindet dbxTPL, dbxDB, DD, FD, dbxForm, dbxReport, Modul-Inclusion, ajax.js und confirm.js in einem zusammenhängenden Ablauf.
Das Beispiel ist nicht nur Pseudocode. Die installierbare Referenz liegt unter dbx/modules/myInvoices/. Die Codeblöcke dieses Kapitels erklären die entscheidenden Verträge; die Moduldateien, Fixtures und ausführbaren Tests sind die maßgebliche, gemeinsam gepflegte Fassung.
Die Wörter MUSS, DARF NICHT, SOLL und DARF sind normativ:
- MUSS und DARF NICHT beschreiben den dbxapp-Vertrag.
- SOLL ist die Standardentscheidung; eine Abweichung braucht einen dokumentierten fachlichen Grund.
- DARF bezeichnet eine unterstützte Variante.
Das Zielbild
Die erste Listenstufe zeigt Rechnungen. Direkt unter jeder Rechnungszeile ruft ein serverseitiger Modulmarker die zweite Listenstufe auf:
Die zweite Stufe zeigt die Artikelpositionen genau dieser Rechnung. Ihr Record-Callback berechnet für jede Position Menge * Einzelpreis, setzt die virtuelle Spalte sum und sammelt Centbeträge. Ihr Footer-Callback setzt die Endsumme und berechnet den benötigten colspan aus der tatsächlich von dbxReport aufgebauten Spaltenzahl.
Der [modul=...]-Aufruf ist kein zweiter Browserrequest. Der dbxInterpreter setzt die angegebenen Parameter als geschützte Modulvariablen, ruft den Modulrouter im aktuellen Owner-Kontext auf und ersetzt den Marker serverseitig durch das Ergebnis. Modulzugriff und DD-Rechte bleiben aktiv.
confirm.js entscheidet nur, ob eine Benutzeraktion fortgesetzt wird. ajax.js transportiert den Request und ersetzt HTML. Berechtigung, Validierung, Transaktion und Mutation bleiben vollständig auf dem Server.
Welche Schicht entscheidet was?
| Frage | Zuständige Schicht | Nicht dort ablegen |
|---|---|---|
| Welche Route läuft? | Modulrouter | SQL, Formularaufbau, Markup |
| Welche fachliche Operation läuft? | Service | Layoutdetails |
| Woher kommen Daten? | dbxDB über DD | PDO oder SQL im Modul |
| Wie sehen Tabellen und Rechte aus? | DD | Controller-Arrays |
| Welche Felder zeigt ein Formular? | FD | zweite Tabellenbeschreibung |
| Wie wird eingegeben und validiert? | dbxForm | direkte $_POST-Speicherung |
| Wie werden beide Stufen gelistet? | je ein dbxReport | eigene Tabellenloops |
| Wie wird eine Unterliste eingebettet? | [modul=...]...[/modul] | interner HTTP-Aufruf |
| Wie wird HTML strukturiert? | dbxTPL-Template | große PHP-HTML-Strings |
| Wie werden Zeilenwerte berechnet? | Record-Callback | Zusatzschleife nach dem Report |
| Wie wird die Endsumme gesetzt? | Footer-Callback | JavaScript-Nachberechnung |
| Wer darf mutieren? | Modulzugriff, DD-Rechte, ggf. Action-Token | Confirm-Dialog |
Warum die großen Kernelklassen nicht nach Größe geteilt werden
dbxDB, dbxDD, dbxForm und dbxReport sind stabile Systemfassaden und Pipelines. Ihre Größe folgt ihrer Fähigkeitstiefe:
- dbxDB vereinheitlicht Server, DD-Auflösung, Rechte, Owner-Filter, Transaktionen, Trace, Fehler und Performance.
- dbxDD erweitert den Datenpfad um Schema-, Backup-, Restore- und Transferprozesse.
- dbxForm hält einen vollständigen Formularzustand von FD/DD bis Validierung, Meldung, Submit-Schutz und Rendern.
- dbxReport erweitert dbxForm um Selection-Felder, Pagination, Zeilenaktionen, Auswahlzustand, Callbacks und mehrere Ausgabemodi.
Eine Kernelaufteilung ist nur mit stabiler Fassade, Regressionstests und einer bewiesenen Verantwortungsgrenze sinnvoll. Fachmodule dürfen dagegen echte Fachlogik in Services oder Repositories gliedern. Sie dürfen Kernel-Funktionen nicht durch eigene Methoden nachbauen, die nur dbx() weiterreichen.
Verzeichnisstruktur des Referenzmoduls
Die zwei Tabellen liegen auf demselben DD-Server. Dadurch kann eine Mehrtabellenmutation über eine gemeinsame dbxDB-Transaktion laufen.
Routen- und Sicherheitsvertrag
| Route | Zweck | Zustand ändert sich? | Schutz |
|---|---|---|---|
| dbx_run1=report | Rechnungen, Filter, Pagination | nein | Modul- und DD-Leserecht |
| dbx_run1=positions&invoice_id=17 | eingebettete Positionen | nein | Modul- und beide DD-Leserechte |
| dbx_run1=form&rid=17 | Rechnungskopf bearbeiten | POST ja | dbxForm plus DD-Schreibrecht |
| dbx_run1=delete&rid=17 | Rechnung samt Positionen löschen | ja | Modulrecht, DD-Löschrecht, Action-Token |
| dbx_run1=install | DD-Sync und idempotente Demo-Fixtures | POST ja | Adminrecht und dbxForm |
Normale Navigation, Filter, Report und Positions-Inclusion bleiben tokenlos. Ein Standardformular erhält keinen zusätzlichen dbx_token, weil dbxForm seinen Submit schützt. Bei der schreibenden Delete-URL erkennt dbxApi::action_url() die Kombination aus delete und rid automatisch, bindet die RID und ergänzt den Token. dbxWebApp prüft ihn vor dem Aufruf des Moduls. Eine action_routes-Konfiguration ist dafür nicht nötig.
DD- und Modulrechte beantworten, wer handeln darf. Der Action-Token beweist zusätzlich, dass der mutierende GET aus dem aktuellen Browserkontext stammt. Er ersetzt keine Rechteprüfung.