# Workflow Orchestration mit Temporal **Meta-Description:** Workflow Orchestration für komplexe Business Logic. Temporal.io, Durable Workflows, Sagas und Error Recovery. **Keywords:** Workflow Orchestration, Temporal, Durable Workflows, Saga Pattern, Business Process, Error Recovery --- ## Einführung Komplexe Business-Prozesse brauchen mehr als einfache Funktionen. **Workflow Orchestration** mit Temporal bietet durable Execution, automatische Retries und State Management für langlebige Prozesse. --- ## Warum Workflow Orchestration? ``` ┌─────────────────────────────────────────────────────────────┐ │ WORKFLOW ORCHESTRATION │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Probleme ohne Orchestration: │ │ ├── Verteilte Transaktionen │ │ ├── Timeout/Failure Handling │ │ ├── State Management über Zeit │ │ ├── Retries mit Exponential Backoff │ │ └── Compensating Transactions (Rollbacks) │ │ │ │ Temporal Lösung: │ │ ├── Durable Execution (überleben Crashes) │ │ ├── Automatische Retries │ │ ├── Timeouts auf allen Ebenen │ │ ├── Saga Pattern Support │ │ └── Workflow as Code (TypeScript) │ │ │ │ Use Cases: │ │ ├── E-Commerce: Order Processing │ │ ├── Finance: Payment Processing │ │ ├── HR: Onboarding Workflows │ │ ├── DevOps: CI/CD Pipelines │ │ └── Data: ETL Pipelines │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Temporal Setup ```bash # Temporal Server (Docker) docker run --rm -p 7233:7233 temporalio/auto-setup # SDK installieren npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity ``` ```typescript // src/activities.ts import { proxyActivities, defineActivity } from '@temporalio/workflow'; // Activities sind normale async Funktionen export const activities = { async sendEmail(to: string, subject: string, body: string): Promise { console.log(`Sending email to ${to}`); // Resend, SendGrid, etc. await resend.emails.send({ to, subject, html: body }); return true; }, async chargePayment(userId: string, amount: number): Promise { console.log(`Charging ${amount} for user ${userId}`); // Stripe, PayPal, etc. const charge = await stripe.charges.create({ amount: Math.round(amount * 100), currency: 'eur', customer: userId }); return charge.id; }, async reserveInventory(productId: string, quantity: number): Promise { console.log(`Reserving ${quantity} of ${productId}`); // Inventory Service const reservation = await inventoryService.reserve(productId, quantity); return reservation.id; }, async releaseInventory(reservationId: string): Promise { console.log(`Releasing reservation ${reservationId}`); await inventoryService.release(reservationId); }, async refundPayment(chargeId: string): Promise { console.log(`Refunding charge ${chargeId}`); await stripe.refunds.create({ charge: chargeId }); }, async createShipment(orderId: string, address: Address): Promise { console.log(`Creating shipment for order ${orderId}`); const shipment = await shippingService.create({ orderId, address }); return shipment.trackingNumber; } }; export type Activities = typeof activities; ``` --- ## Workflow Definition ```typescript // src/workflows/order-processing.ts import { proxyActivities, sleep, defineSignal, defineQuery, setHandler, condition, ApplicationFailure } from '@temporalio/workflow'; import type { Activities } from '../activities'; // Activity Proxies mit Retry Config const { sendEmail, chargePayment, reserveInventory, releaseInventory, refundPayment, createShipment } = proxyActivities({ startToCloseTimeout: '1 minute', retry: { initialInterval: '1 second', backoffCoefficient: 2, maximumAttempts: 5, nonRetryableErrorTypes: ['InvalidPaymentError'] } }); // Signals für externe Events const cancelOrderSignal = defineSignal('cancelOrder'); const orderShippedSignal = defineSignal<[string]>('orderShipped'); // Queries für Status-Abfragen const getStatusQuery = defineQuery('getStatus'); interface OrderInput { orderId: string; userId: string; products: Array<{ productId: string; quantity: number }>; totalAmount: number; shippingAddress: Address; } interface OrderStatus { state: 'pending' | 'paid' | 'reserved' | 'shipped' | 'cancelled' | 'failed'; chargeId?: string; reservationIds?: string[]; trackingNumber?: string; error?: string; } export async function orderProcessingWorkflow(input: OrderInput): Promise { let status: OrderStatus = { state: 'pending' }; let cancelled = false; // Signal Handler setHandler(cancelOrderSignal, () => { cancelled = true; }); // Query Handler setHandler(getStatusQuery, () => status); try { // 1. Payment status.chargeId = await chargePayment(input.userId, input.totalAmount); status.state = 'paid'; if (cancelled) { await compensatePayment(status.chargeId); status.state = 'cancelled'; return status; } // 2. Inventory Reservation status.reservationIds = []; for (const product of input.products) { const reservationId = await reserveInventory(product.productId, product.quantity); status.reservationIds.push(reservationId); } status.state = 'reserved'; if (cancelled) { await compensate(status); status.state = 'cancelled'; return status; } // 3. Shipment status.trackingNumber = await createShipment(input.orderId, input.shippingAddress); status.state = 'shipped'; // 4. Confirmation Email await sendEmail( input.userId, 'Ihre Bestellung wurde versandt', `Tracking: ${status.trackingNumber}` ); return status; } catch (error) { // Compensating Transactions await compensate(status); status.state = 'failed'; status.error = (error as Error).message; // Notify Customer await sendEmail( input.userId, 'Problem mit Ihrer Bestellung', 'Bitte kontaktieren Sie unseren Support.' ); throw error; } } // Saga Compensation async function compensate(status: OrderStatus) { // Reverse order: Shipment → Inventory → Payment // Release Inventory if (status.reservationIds) { for (const reservationId of status.reservationIds) { await releaseInventory(reservationId); } } // Refund Payment if (status.chargeId) { await compensatePayment(status.chargeId); } } async function compensatePayment(chargeId: string) { await refundPayment(chargeId); } ``` --- ## Worker Setup ```typescript // src/worker.ts import { Worker } from '@temporalio/worker'; import { activities } from './activities'; async function run() { const worker = await Worker.create({ workflowsPath: require.resolve('./workflows'), activities, taskQueue: 'order-processing', // Worker Options maxConcurrentActivityExecutions: 10, maxConcurrentWorkflowTaskExecutions: 5 }); console.log('Worker started'); await worker.run(); } run().catch((err) => { console.error(err); process.exit(1); }); ``` --- ## Client Usage ```typescript // src/client.ts import { Client, Connection } from '@temporalio/client'; import { orderProcessingWorkflow } from './workflows/order-processing'; async function main() { const connection = await Connection.connect({ address: 'localhost:7233' }); const client = new Client({ connection }); // Workflow starten const handle = await client.workflow.start(orderProcessingWorkflow, { taskQueue: 'order-processing', workflowId: `order-${orderId}`, args: [{ orderId: '12345', userId: 'user-789', products: [ { productId: 'prod-1', quantity: 2 }, { productId: 'prod-2', quantity: 1 } ], totalAmount: 99.99, shippingAddress: { street: 'Hauptstraße 1', city: 'Berlin', zip: '10115' } }] }); console.log(`Started workflow: ${handle.workflowId}`); // Status abfragen const status = await handle.query('getStatus'); console.log('Current status:', status); // Auf Completion warten const result = await handle.result(); console.log('Workflow completed:', result); } // Order canceln async function cancelOrder(workflowId: string) { const handle = client.workflow.getHandle(workflowId); await handle.signal('cancelOrder'); } ``` --- ## API Route Integration ```typescript // app/api/orders/route.ts import { NextRequest, NextResponse } from 'next/server'; import { Client, Connection } from '@temporalio/client'; import { orderProcessingWorkflow } from '@/workflows/order-processing'; let client: Client; async function getClient() { if (!client) { const connection = await Connection.connect({ address: process.env.TEMPORAL_ADDRESS || 'localhost:7233' }); client = new Client({ connection }); } return client; } export async function POST(request: NextRequest) { const body = await request.json(); const client = await getClient(); const handle = await client.workflow.start(orderProcessingWorkflow, { taskQueue: 'order-processing', workflowId: `order-${body.orderId}`, args: [body] }); return NextResponse.json({ workflowId: handle.workflowId, status: 'started' }); } export async function GET(request: NextRequest) { const workflowId = request.nextUrl.searchParams.get('workflowId'); if (!workflowId) { return NextResponse.json({ error: 'workflowId required' }, { status: 400 }); } const client = await getClient(); const handle = client.workflow.getHandle(workflowId); try { const status = await handle.query('getStatus'); return NextResponse.json({ workflowId, status }); } catch (error) { return NextResponse.json({ error: 'Workflow not found' }, { status: 404 }); } } ``` --- ## Timer & Scheduling ```typescript // src/workflows/subscription-renewal.ts import { proxyActivities, sleep, continueAsNew } from '@temporalio/workflow'; export async function subscriptionRenewalWorkflow( userId: string, planId: string ): Promise { const { chargeSubscription, sendRenewalReminder, sendRenewalConfirmation } = proxyActivities({ startToCloseTimeout: '1 minute' }); // Reminder 7 Tage vor Ablauf await sleep('23 days'); await sendRenewalReminder(userId); // Warte bis Ablauf await sleep('7 days'); // Charge await chargeSubscription(userId, planId); await sendRenewalConfirmation(userId); // Workflow für nächsten Monat fortsetzen await continueAsNew(userId, planId); } ``` --- ## Error Handling & Compensation ```typescript // Saga Pattern mit expliziter Compensation import { proxyActivities, ApplicationFailure } from '@temporalio/workflow'; interface SagaStep { execute: () => Promise; compensate: (result: T) => Promise; } async function runSaga(steps: SagaStep[]): Promise { const results: T[] = []; const completedSteps: SagaStep[] = []; try { for (const step of steps) { const result = await step.execute(); results.push(result); completedSteps.push(step); } return results; } catch (error) { // Compensate in reverse order for (let i = completedSteps.length - 1; i >= 0; i--) { const step = completedSteps[i]; const result = results[i]; try { await step.compensate(result); } catch (compensationError) { console.error('Compensation failed:', compensationError); // Log aber nicht re-throw } } throw error; } } // Verwendung export async function bookingWorkflow(input: BookingInput) { const results = await runSaga([ { execute: () => reserveHotel(input.hotelId, input.dates), compensate: (reservationId) => cancelHotelReservation(reservationId) }, { execute: () => reserveFlight(input.flightId), compensate: (reservationId) => cancelFlightReservation(reservationId) }, { execute: () => chargeCustomer(input.customerId, input.total), compensate: (chargeId) => refundCustomer(chargeId) } ]); return { hotelReservation: results[0], flightReservation: results[1], chargeId: results[2] }; } ``` --- ## Fazit Temporal Workflow Orchestration bietet: 1. **Durability**: Workflows überleben Server-Crashes 2. **Visibility**: Vollständige History & State 3. **Reliability**: Automatische Retries & Timeouts 4. **Scalability**: Horizontale Worker-Skalierung Für komplexe Business-Prozesse unverzichtbar. --- ## Bildprompts 1. "Workflow steps connected with arrows, showing state persistence" 2. "Saga pattern with forward and backward arrows, compensation flow" 3. "Multiple workers processing workflow tasks, distributed orchestration" --- ## Quellen - [Temporal.io Documentation](https://docs.temporal.io/) - [Temporal TypeScript SDK](https://typescript.temporal.io/) - [Saga Pattern](https://microservices.io/patterns/data/saga.html) - [Durable Execution](https://temporal.io/blog/building-reliable-distributed-systems-in-node)