Skip to Content
Low-CodeUse CasesWorkflow-Integration

Workflow-Integration

Die Workflow-Integration verbindet LowCode nahtlos mit der Engine. Sie können BPMN-Prozesse starten, auf Events reagieren, UserTasks bearbeiten und komplexe End-to-End-Workflows orchestrieren — alles innerhalb Ihrer Node-RED Flows.

Verfügbare Engine-Nodes

Das ProcessCube® Enterprise Image stellt folgende Nodes für die Engine-Integration bereit:

NodeFunktionRichtung
process-startBPMN-Prozess startenFlow → Engine
usertask-event-listenerAuf neue UserTasks reagierenEngine → Flow
usertask-outputUserTask-Ergebnis an Engine sendenFlow → Engine
process-event-listenerEngine-Events abonnierenEngine → Flow
query-process-instancesProzess-Instanzen abfragenFlow → Engine
signal-eventSignal-Events an Engine sendenFlow → Engine

Prozesse starten

Einfacher Prozessstart

[http in: POST /api/start-process] → [fn: preparePayload] → [process-start: MyProcess_v1] → [fn: formatResponse] → [http response]
// Function Node: preparePayload const body = msg.payload; // Prozess-Parameter setzen msg.processModelId = "ApprovalProcess_v1"; msg.payload = { applicantName: body.name, requestType: body.type, amount: body.amount }; return msg;
// Function Node: formatResponse msg.statusCode = 201; msg.payload = { success: true, processInstanceId: msg.processInstanceId, message: "Prozess wurde gestartet" }; return msg;

Prozessstart mit Korrelation

Wenn Sie das Ergebnis eines gestarteten Prozesses abwarten möchten, verwenden Sie eine Korrelations-ID:

[http in: POST /api/request] → [fn: generateCorrelationId] → [process-start: RequestProcess] → [fn: storeCorrelation] → [http response: 202 Accepted]
// Function Node: generateCorrelationId const correlationId = `corr-${Date.now()}-${Math.random().toString(36).slice(2)}`; msg.correlationId = correlationId; msg.payload = { ...msg.payload, correlationId: correlationId }; return msg;

UserTask-Handling

Das Herzstück der Workflow-Integration ist das UserTask-Handling. Dabei wartet die Engine auf eine menschliche Eingabe, die über LowCode bereitgestellt wird.

UserTask-Lifecycle

1. Engine erstellt UserTask → [usertask-event-listener] empfängt Event 2. LowCode zeigt Formular → [ui-dynamic-form] rendert FormFields 3. Benutzer füllt Formular aus → [usertask-output] sendet Ergebnis 4. Engine setzt Prozess fort → Nächster Schritt im BPMN

UserTask empfangen und anzeigen

[usertask-event-listener: userTaskCreated] → [fn: enrichTaskData] → [fn: storeTask] → [ui-dynamic-form: Formular anzeigen]
// Function Node: enrichTaskData const userTask = msg.payload; // UserTask-Daten für die Anzeige aufbereiten msg.taskDisplay = { id: userTask.id, name: userTask.name, processModelId: userTask.processModelId, processInstanceId: userTask.processInstanceId, formFields: userTask.formFields, createdAt: new Date().toISOString() }; // Im Flow-Context speichern für späteren Zugriff const openTasks = flow.get("openTasks") || []; openTasks.push(msg.taskDisplay); flow.set("openTasks", openTasks); node.status({ fill: "blue", shape: "dot", text: `${openTasks.length} offene Tasks` }); return msg;

UserTask beenden

[ui-dynamic-form: Ergebnis] → [fn: prepareFinish] → [usertask-output: UserTask beenden] → [fn: cleanUp] → [ui-notification: "Aufgabe erledigt"]
// Function Node: prepareFinish const formResult = msg.payload; const taskId = msg.userTaskInstanceId; // Ergebnis für die Engine vorbereiten msg.userTaskInstanceId = taskId; msg.payload = { approved: formResult.approved, comment: formResult.comment || "", processedBy: formResult.processedBy }; return msg;
// Function Node: cleanUp const taskId = msg.userTaskInstanceId; // Erledigten Task aus der Liste entfernen const openTasks = flow.get("openTasks") || []; const updated = openTasks.filter(t => t.id !== taskId); flow.set("openTasks", updated); msg.payload = `Aufgabe ${taskId.substring(0, 8)} erledigt`; node.status({ fill: "green", shape: "dot", text: `${updated.length} offene Tasks` }); return msg;

Engine-Events abonnieren

Mit dem process-event-listener-Node reagieren Sie auf alle Events der Engine.

Verfügbare Event-Typen

EventBeschreibungTypischer Einsatz
processStartedProzess wurde gestartetLogging, Monitoring
processFinishedProzess wurde beendetBenachrichtigungen
processErrorFehler im ProzessAlerting, Error Handling
userTaskCreatedNeuer UserTask erstelltUI-Anzeige
userTaskFinishedUserTask wurde beendetStatistiken
signalReceivedSignal-Event empfangenCross-Prozess-Kommunikation

Monitoring-Flow

[process-event-listener: processStarted] → [fn: logStart] → [fn: updateDashboard] → [ui-chart: Prozesse pro Stunde] [process-event-listener: processError] → [fn: formatAlert] → [email: Ops-Team benachrichtigen]
// Function Node: logStart const event = msg.payload; // Statistik aktualisieren const stats = flow.get("processStats") || { started: 0, finished: 0, errors: 0 }; stats.started++; flow.set("processStats", stats); // Für Dashboard aufbereiten msg.payload = stats.started; msg.topic = "Gestartete Prozesse"; return msg;

Prozess-Instanzen abfragen

Fragen Sie den aktuellen Status von Prozess-Instanzen ab:

[http in: GET /api/processes] → [query-process-instances] → [fn: formatResult] → [http response]
// Function Node: formatResult const instances = msg.payload || []; msg.statusCode = 200; msg.payload = { success: true, count: instances.length, data: instances.map(pi => ({ id: pi.id, processModelId: pi.processModelId, state: pi.state, startedAt: pi.createdAt, finishedAt: pi.finishedAt || null })) }; return msg;

Vollständiges E2E-Workflow-Beispiel

Das folgende Beispiel zeigt einen kompletten End-to-End-Workflow: Ein HTTP-Request startet einen Genehmigungsprozess, ein Sachbearbeiter bearbeitet den UserTask über ein dynamisches Formular, und das Ergebnis wird als HTTP-Response zurückgegeben.

BPMN-Prozess: Genehmigung

[Start] → [UserTask: Antrag prüfen] → [Gateway: Genehmigt?] ├─ Ja → [ServiceTask: Bestätigung senden] → [End] └─ Nein → [ServiceTask: Absage senden] → [End]

Flow 1: Prozess starten (HTTP-Trigger)

[http in: POST /api/approval] → [fn: validateRequest] → [fn: prepareProcess] → [process-start: ApprovalProcess] → [fn: storeCorrelation] → [http response: 202 Accepted]
// Function Node: validateRequest const body = msg.payload; if (!body.applicant || !body.amount) { msg.statusCode = 400; msg.payload = { success: false, error: "Felder 'applicant' und 'amount' sind erforderlich" }; return [null, msg]; // Zweiter Ausgang: Fehler } return [msg, null]; // Erster Ausgang: Weiter
// Function Node: prepareProcess msg.processModelId = "ApprovalProcess_v1"; msg.correlationId = `approval-${Date.now()}`; msg.payload = { applicant: msg.payload.applicant, amount: msg.payload.amount, reason: msg.payload.reason || "", correlationId: msg.correlationId }; return msg;

Flow 2: UserTask bearbeiten (Dashboard)

[usertask-event-listener: userTaskCreated] → [fn: filterApprovalTasks] → [fn: prepareFormData] → [ui-dynamic-form: Antrag prüfen] → [fn: mapFormResult] → [usertask-output: Ergebnis senden] → [fn: notifyComplete] → [ui-notification: "Antrag bearbeitet"]
// Function Node: filterApprovalTasks const userTask = msg.payload; // Nur UserTasks des Genehmigungsprozesses verarbeiten if (userTask.processModelId !== "ApprovalProcess_v1") { return null; // Flow für andere Tasks stoppen } return msg;
// Function Node: prepareFormData const userTask = msg.payload; msg.userTaskInstanceId = userTask.id; msg.userTaskConfig = { userTaskInstanceId: userTask.id, title: `Antrag von ${userTask.tokens[0]?.payload?.applicant || "Unbekannt"}`, formFields: userTask.formFields }; return msg;
// Function Node: mapFormResult const formResult = msg.payload; msg.payload = { approved: formResult.decision === "approved", comment: formResult.comment || "", reviewedAt: new Date().toISOString() }; return msg;

Flow 3: Ergebnis abfragen (Status-API)

[http in: GET /api/approval/:correlationId] → [query-process-instances] → [fn: findByCorrelation] → [fn: formatStatus] → [http response]
// Function Node: findByCorrelation const correlationId = msg.req.params.correlationId; const instances = msg.payload || []; const instance = instances.find( pi => pi.correlationId === correlationId ); if (!instance) { msg.statusCode = 404; msg.payload = { success: false, error: "Antrag nicht gefunden" }; return [null, msg]; } msg.processInstance = instance; return [msg, null];
// Function Node: formatStatus const pi = msg.processInstance; msg.statusCode = 200; msg.payload = { success: true, data: { correlationId: msg.req.params.correlationId, state: pi.state, approved: pi.tokens?.[0]?.payload?.approved ?? null, startedAt: pi.createdAt, finishedAt: pi.finishedAt || null } }; return msg;

Best Practices

1. Events statt Polling

Nutzen Sie die Event-Listener der Engine anstatt regelmäßig nach neuen UserTasks zu fragen:

// Korrekt: Event-basiert [usertask-event-listener] → [Verarbeitung] // Vermeiden: Polling [inject: Alle 5s] → [query-user-tasks] → [Verarbeitung]

2. Fehlerbehandlung im Prozess

Fangen Sie Fehler ab und geben Sie sie an die Engine zurück:

[catch: Engine-Nodes] → [fn: logError] → [fn: sendErrorToEngine] → [debug]

3. Korrelation nutzen

Verwenden Sie Korrelations-IDs, um HTTP-Requests mit Prozess-Instanzen zu verknüpfen. So können Clients den Status asynchroner Prozesse abfragen.

4. UserTasks im Context verwalten

Speichern Sie offene UserTasks im Flow-Context, um sie in Dashboard-Tabellen und Statistiken anzuzeigen:

// Offene Tasks pflegen const tasks = flow.get("openTasks") || []; tasks.push(newTask); flow.set("openTasks", tasks);

5. Environment Variables für Engine-URLs

Konfigurieren Sie die Engine-URL immer als Environment Variable:

const engineUrl = env.get("ENGINE_URL"); // z.B. http://engine:8000

Nächste Schritte