Architektur-Überblick¶
KCForms besteht aus drei Teilen: einem Frontend, das im Browser läuft, einem Spring-Boot-Backend, das Formulare definiert, ausliefert, validiert, druckt und speichert, und einer Haupt-Datenbank, in der sowohl die Formular-Definitionen als auch die ausgefüllten Datensätze liegen.
Als Haupt-Datenbank kann KCForms MySQL 8, PostgreSQL 16, Oracle oder Microsoft SQL Server nutzen. Welche es ist, erkennt das Backend beim Start an der JDBC-Adresse. Details stehen unter Datenbank.
Für die Entwicklung kommen dazu:
- MailPit als lokaler SMTP-Fänger — Mail-Skripte erreichen in der Entwicklung nie echte Empfänger.
- Bind-Mounts für Logs, das Skript-Dateisystem, Assets, Datei-Bibliotheken und den Formular-Export. Alles, was im Container entsteht, ist direkt im Explorer bzw. Finder sichtbar.
Komponentenbild¶
flowchart LR
subgraph Browser
FE["Frontend<br/>React + Vite<br/>TypeScript, Tailwind"]
end
subgraph Server
BE["Backend<br/>Spring Boot 3.3<br/>Java 21, GraalVM JS<br/>PDFBox + Batik"]
end
DB[("Haupt-Datenbank<br/>MySQL / PostgreSQL /<br/>Oracle / SQL Server")]
FS[["Bind-Mounts<br/>logs · fs · assets ·<br/>script-libs · forms-export"]]
MAIL["MailPit (SMTP)<br/>UI :8025"]
EXT["Externe Systeme<br/>LDAP · JDBC · HTTP/SOAP"]
FE -- "HTTPS / JSON" --> BE
BE -- JDBC --> DB
BE --- FS
BE -- SMTP --> MAIL
BE <--> EXT
Im Betrieb gibt es zwei Varianten:
- Docker: ein fertiges Paket mit Tomcat und Datenbank — siehe Installation mit Docker.
- WAR auf Tomcat 10: Frontend und Backend in einer Datei
ROOT.war(gebaut mitDockerfile.war); das Backend liefert die Oberfläche unter/selbst aus.
Welche Daten liegen wo?¶
KCForms folgt dem Prinzip „eine Tabelle pro Formular". Beim Anlegen oder Speichern einer Formular-Definition gleicht das Backend die zugehörige Tabelle mit den Feldern des Schemas ab (fehlende Spalten kommen dazu). Das hat zwei Vorteile:
- SQL-Direktzugriff — die ausgefüllten Daten lassen sich ohne Umweg über JSON-Spalten auswerten.
- Änderungen bleiben lokal — ein neues Feld betrifft nur die Tabelle dieses einen Formulars.
| Tabelle | Inhalt |
|---|---|
form_<id> |
ausgefüllte Datensätze eines Formulars |
form_<id>_<feld> |
Detail-Tabellen (kc:childTable); bei tiefer Verschachtelung form_<id>_<feld>_<unterfeld> |
form_definition |
Schema, Skripte, Metadaten der Formulare |
form_version_snapshot |
eingefrorene Formular-Stände (Versionierung) |
form_attachment |
Datei-Anlagen |
form_property |
Properties je Formular und global |
section_def, section_snapshot |
Section-Repository (zentrale Feldgruppen) |
USERS, GROUPS, USER_GROUPS |
Benutzer und Gruppen |
permissions, group_permissions, form_acl |
Rechte |
auth_script |
Skripte für die Anmeldung per Skript |
app_settings |
Laufzeit-Einstellungen (Mail, Anmeldung, Vorgaben) |
audit_event, form_audit_log |
Protokoll (Anmeldungen, Änderungen) |
admin_script |
gespeicherte Skripte der Skript-Konsole |
mail_outbox |
E-Mail-Ausgang (Warteschlange mit Wiederholung) |
scheduled_job, scheduled_job_run |
Zeitplaner |
id_encoder |
Tokens für Ad-hoc-Links |
Groß- und Kleinschreibung
USERS, GROUPS und USER_GROUPS heißen aus historischen Gründen in
Großbuchstaben. Auf MySQL spielt das keine Rolle; auf den anderen
Datenbanken kümmert sich die Dialekt-Schicht darum.
Skripting im Backend¶
Skripte sind JavaScript und laufen im Backend in einer
GraalVM-Sandbox. Ein Skript sieht nur die Objekte, die KCForms ihm
mitgibt (ctx, db, mail, fs, http …). Beliebige Java-Klassen
nachladen, direkt auf Dateien oder das Netz zugreifen oder Threads starten
kann es nicht — Dateien und HTTP gehen nur über die freigegebenen
Bindings mit ihren eigenen Grenzen (Sandbox-Ordner, Adress-Freigabeliste).
Jeder Lauf hat zudem ein Zeitlimit. Das ist die zentrale Sicherheitslinie.
| Hook / Art | Wann läuft er? |
|---|---|
onLoad |
Formular wird geöffnet (Vorbelegung, Daten ziehen) |
onFieldChange |
ein Feld hat seinen Wert geändert |
onValidate |
vor dem Speichern — Geschäftsregeln |
onBeforeSave / onAfterSave |
rund um das Speichern (vorher mit Veto, nachher mit ctx.isNew) |
onSubmit |
nach dem Speichern — Mails, PDFs, Dateiausgabe |
onBeforeDelete / onAfterDelete |
rund um das Löschen |
| eigene Hooks | über Schaltflächen (kc:button) aufrufbar |
| Anmelde-Skripte | bei der Anmeldung per Skript (scriptLogin) |
| Jobs | vom Zeitplaner gestartet |
| REST-Services | eigene HTTP-Endpunkte — siehe REST-Services |
Reihenfolge und Details: Lifecycle-Hooks und Scripting-Überblick.
Formular-Editor¶
Der Formular-Editor (im Quelltext „Form-Studio") ist die Oberfläche, in der Formulare entstehen. Damit legt ein Formular-Autor ohne neues Deployment Felder an, ordnet sie, stattet sie mit Lookups, Pickern, Sichtbarkeitsregeln, Berechnungen und Skripten aus und legt das Druckbild fest. Beim Speichern gleicht das Backend sofort die Datenbank-Tabelle ab — ein neues Feld hat unmittelbar seine Spalte.
Sicherheits-Schichten¶
flowchart TB
B["Browser"] --> API["/api/**"]
API --> SS["Spring Security"]
SS --> AUTH["Anmelde-Strategie<br/>dbLogin · fixedLogin · scriptLogin ·<br/>bankid · oidc"]
AUTH --> RBAC["Rollen und Rechte<br/>ROLE_* (Gruppen) + PERM_* (Berechtigungen)"]
RBAC --> ROW["Formular- und Datensatz-Ebene<br/>Formular-Freigaben (form_acl),<br/>eigene Datensätze; Admin mit scope=all"]
- Anmeldung: austauschbare Strategien — siehe Anmeldung.
- Autorisierung: Gruppen werden zu Spring-Authorities mit dem Präfix
ROLE_*, Berechtigungen zuPERM_*. Ein Admin erhält automatisch allePERM_*. - Datensatz-Ebene: Listen zeigen die eigenen Datensätze; ein Admin
sieht mit
?scope=allalle.
Was läuft beim Start?¶
- Die Datenbank ist erreichbar; das Backend erkennt den Hersteller an der JDBC-Adresse.
- Flyway spielt das Kern-Schema ein (
db/migration/<hersteller>/). Auf MySQL ergänzenschema.sqlundSchemaMigrationszusätzlich fehlende Tabellen und Spalten — beides ist idempotent. - Seeder legen Standard-Rechte, Standard-Gruppen, Baukasten-Elemente und — falls eingeschaltet — Demo-Formulare an.
FormTableInitializerlegt für jedes Formular die Tabelle an oder ergänzt fehlende Spalten.