Zwei Systeme, dieselben Events
dForge hat zwei event-getriebene Systeme, die dieselben Datensatz-Events beobachten, beim Auslösen aber Unterschiedliches tun:
| System | Konfig-Datei | Was passiert | Anwendungsfall |
|---|---|---|---|
| Trigger | logic/triggers.json | Führt eine DSL-Aktion aus (synchron oder asynchron) | Interne Automatisierung: Benachrichtigungen, Kaskaden, Workflows |
| Webhooks | logic/webhooks.json | Sendet einen HTTP-POST an eine externe URL | Externe Integration: Slack, Zapier, eigene Services |
Beide lösen bei denselben Event-Typen aus: insert, update, delete, status_change und any.
Event-Typen
| Event | Löst aus, wenn | Alte Werte verfügbar |
|---|---|---|
insert | Ein neuer Datensatz angelegt wird | Nein |
update | Ein beliebiges Feld eines bestehenden Datensatzes sich ändert | Ja |
delete | Ein Datensatz gelöscht wird | Ja (die gelöschte Zeile) |
status_change | Der Wert eines bestimmten Feldes sich ändert (vergleicht alt vs. neu) | Ja |
any | Alle obigen | Hängt vom Event ab |
Trigger
triggers.json
{
"triggers": [
{
"code": "on_opportunity_won",
"description": "Owner benachrichtigen, wenn Opportunity als gewonnen geschlossen wird",
"entity": "opportunity",
"event": "status_change",
"condition": "[stage] = 'Closed Won'",
"action": "on_won_notify",
"async": true
}
]
}
| Feld | Pflicht | Beschreibung |
|---|---|---|
code | Ja | Eindeutiger Identifier innerhalb des Moduls |
entity | Ja | Zu beobachtende Entität (kann auf Entitäten anderer Module verweisen) |
event | Ja | Eines von insert, update, delete, status_change, any |
condition | Nein | Formel, die gegen Datensatzdaten ausgewertet wird — Trigger löst nur aus, wenn true |
action | Ja | Auszuführender Aktions-Code (muss in ui/actions.json definiert sein) |
async | Nein | true (Default) reiht einen Hintergrund-Job ein; false läuft in derselben Transaktion |
params | Nein | Statische Parameter, die an die Aktion übergeben werden |
enabled | Nein | Default true. Auf false setzen, um ohne Entfernen zu deaktivieren |
Bedingungen
Bedingungen verwenden dieselbe Formel-Syntax wie canExecute auf Aktionen:
[status] = 'Active'
[amount] > 1000
[stage] = 'Closed Won' AND [priority] = 'High'
Die Bedingung wird zur Modul-Installationszeit geparst und zur Event-Zeit gegen den Datensatz ausgewertet. Wenn sie nicht passt, löst der Trigger nicht aus (fail-closed).
Synchron vs. asynchron
Asynchrone Trigger ("async": true, der Default):
- Reihen eine Zeile in die
background_action-Tabelle ein - Ein Hintergrund-Worker greift sie innerhalb von ~10 Sekunden auf
- Laufen in ihrer eigenen Transaktion
- Keine Deadlocks, keine Kaskaden innerhalb der ursprünglichen Transaktion
- Verwenden Sie sie für Benachrichtigungen, E-Mails, Logs, nicht-kritische Seiteneffekte
Synchrone Trigger ("async": false):
- Werden sofort ausgeführt, innerhalb der ursprünglichen Transaktion
- Atomar mit der ursprünglichen Mutation — wenn der Trigger fehlschlägt, wird die ganze Änderung zurückgerollt
- Risiko von Endlosschleifen, daher erzwingt die Plattform eine maximale Verschachtelungstiefe von 5 und eine pro-Datensatz-Re-Entrancy-Schutz
- Verwenden Sie sie für kaskadierende Feld-Updates, Validierung, abgeleitete Zustände
Trigger-only Aktionen
Wenn eine Aktion nur von Triggern (niemals von einem UI-Button) ausgelöst werden soll, setzen Sie ihr canExecute auf false. Hintergrund-Trigger umgehen canExecute, sodass die Aktion vom Trigger trotzdem läuft, der Button in der Oberfläche aber versteckt ist:
canExecute:
false
execute:
notify([owner_id], 'Automatisch: ' + [title] + ' abgeschlossen')
Webhooks
webhooks.json
{
"subscriptions": [
{
"code": "on_task_completed",
"entity": "task",
"event": "status_change",
"condition": "[status] = 'completed'",
"endpointUrl": "https://hooks.example.com/dforge",
"secretCd": "webhook_signing_key",
"payload": {
"include": ["task_id", "title", "status"],
"includeOld": true
}
}
]
}
| Feld | Pflicht | Beschreibung |
|---|---|---|
code | Ja | Eindeutiger Identifier innerhalb des Moduls |
entity | Ja | Zu beobachtende Entität |
event | Ja | Event-Typ |
condition | Nein | Formel — löst nur aus, wenn true |
endpointUrl | Nein | HTTP-Endpoint zum POST-en (kann später in der Admin-Oberfläche gesetzt werden) |
secretCd | Nein | Referenz auf ein Geheimnis, das für HMAC-SHA256-Signierung verwendet wird |
payload.include | Nein | Array von Feldern, die enthalten sein sollen — weglassen für alle |
payload.includeOld | Nein | true, um alte Werte für update/delete einzuschließen |
enabled | Nein | Default true |
Payload
{
"deliveryId": "163053801702424576",
"event": "status_change",
"timestamp": "2026-03-26T22:34:25Z",
"subscription": { "code": "on_task_completed", "module": "tasks" },
"folder": { "folderId": 123 },
"entity": "task",
"recordId": { "task_id": 456 },
"data": { "task_id": 456, "title": "My Task", "status": "completed" },
"changes": { "status": { "old": "in_progress", "new": "completed" } }
}
Auslieferung und Wiederholung
- Hintergrund-Service pollt alle 5 Sekunden
- Exponentielles Backoff: sofort → 30s → 2min → 10min → 1h
- Maximal 5 Versuche, dann als
failedmarkiert - Header enthalten
X-DForge-Event,X-DForge-Entity,X-DForge-SubscriptionundX-DForge-Signature - Auslieferungs-Historie wird 30 Tage aufbewahrt
HMAC-Signierung
Wenn secretCd gesetzt ist, wird der Payload mit HMAC-SHA256 signiert und die Signatur in X-DForge-Signature gesendet. Auf der Empfängerseite verifizieren:
HMAC-SHA256(secret, request_body) == X-DForge-Signature
Trigger vs. Webhooks: Was wann verwenden
| Szenario | Verwenden |
|---|---|
| Einen Anwender benachrichtigen, wenn sich ein Datensatz ändert | Trigger → Aktion mit notify() |
| Eine E-Mail bei Statusänderung versenden | Trigger → Aktion mit sendEmail() |
| Einen verbundenen Datensatz aktualisieren | Trigger (synchron) → Aktion mit Feld-Set |
| In Slack/Teams/Discord posten | Webhook |
| Daten zu einem externen CRM/ERP synchronisieren | Webhook |
| Komplexe mehrstufige Geschäftslogik ausführen | Trigger → Aktion mit voller DSL |
Trigger bleiben innerhalb von dForge und haben die gesamte DSL und Datenbank zur Verfügung. Webhooks dienen dem Überschreiten der Grenze zu anderen Systemen.