Claude Code & Apache Kafka: Event-Streaming-Systeme mit KI entwickeln

Moderne Softwarearchitektur dreht sich zunehmend um Events statt um Request-Response. Statt dass Dienste synchron aufeinander warten, produzieren sie Ereignisse — und andere Dienste reagieren darauf, wann immer sie bereit sind. Apache Kafka ist das De-facto-Standard-Tool dafür: ein verteiltes Event-Streaming-System, das Millionen von Nachrichten pro Sekunde verarbeiten kann, Ereignisse dauerhaft speichert und Dienste vollständig entkoppelt.

Das Problem: Kafka hat eine steile Lernkurve. Topics, Partitionen, Consumer Groups, Offsets, Schema Registry — jedes Konzept baut auf dem nächsten auf. Claude Code vereinfacht den Einstieg erheblich: es liest die bestehende Konfiguration, erklärt das Zusammenspiel der Komponenten und generiert korrekten, produktionsreifen Code für Producer, Consumer und Streams. Dieser Artikel zeigt, wie das konkret aussieht.

Claude Code Mastery — Kafka, Agents und produktive Workflows auf Deutsch

Event-driven Architecture ist nur ein Thema: der Kurs zeigt, wie du Claude Code für komplexe Backend-Systeme, autonome Agents und professionelle Entwicklungsworkflows einsetzt. Einmalig bezahlt, kein Abo.

Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

1. Was ist Event-Driven Architecture und warum Kafka?

In einer klassischen Microservices-Architektur ruft Dienst A Dienst B per HTTP auf und wartet auf die Antwort. Das funktioniert — bis Dienst B langsam wird, ausfällt oder überlastet ist. Dann blockiert A, Timeouts erzeugen Fehler, und ein Ausfall pflanzt sich durch das System fort.

Event-driven Architecture dreht das um: Dienst A produziert ein Event ("Bestellung eingegangen") und schreibt es in ein zentrales Log. Dienst B, C und D lesen dieses Log unabhängig voneinander und reagieren — jeder in seinem eigenen Tempo, ohne voneinander zu wissen. Fällt B aus, verpasst er keine Events: er liest sie nach, sobald er wieder läuft.

Kafka ist dafür gebaut. Es speichert Events dauerhaft in Topics, verteilt sie auf Partitionen für Parallelität, und erlaubt beliebig viele Consumer Groups, die unabhängig voneinander denselben Stream lesen — ohne sich gegenseitig zu beeinflussen.

Typische Kafka-Anwendungsfälle: Bestellsysteme (Order-Event → Lager, Zahlung, Versand), Activity-Tracking (Klicks, Views in Echtzeit), Log-Aggregation über viele Services, Change Data Capture aus Datenbanken, und Echtzeit-Analytics-Pipelines.

2. Kafka Setup mit Docker Compose

Für lokale Entwicklung ist Docker Compose die schnellste Lösung. Statt Kafka und ZooKeeper manuell zu installieren und zu konfigurieren, beschreibt eine einzige Datei das komplette Setup. Claude Code generiert diese Datei und erklärt jeden Parameter:

claude "Erstell ein Docker Compose Setup für Kafka mit KRaft-Modus
(ohne ZooKeeper), Schema Registry und Kafka UI für lokale Entwicklung"

Das Ergebnis ist eine vollständige docker-compose.yml:

version: '3.8'
services:
  kafka:
    image: confluentinc/cp-kafka:7.6.0
    hostname: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"

  schema-registry:
    image: confluentinc/cp-schema-registry:7.6.0
    ports:
      - "8081:8081"
    environment:
      SCHEMA_REGISTRY_HOST_NAME: schema-registry
      SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka:9092
    depends_on:
      - kafka

  kafka-ui:
    image: provectuslabs/kafka-ui:latest
    ports:
      - "8080:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
      KAFKA_CLUSTERS_0_SCHEMAREGISTRY: http://schema-registry:8081
    depends_on:
      - kafka
      - schema-registry

Mit docker compose up -d läuft Kafka lokal. Kafka UI ist unter http://localhost:8080 erreichbar und zeigt Topics, Consumer Groups und Messages in einer übersichtlichen Oberfläche.

3. Producer schreiben

Node.js Producer mit kafkajs

Claude Code liest den Docker-Compose-Stack und generiert einen passenden Producer in der gewünschten Sprache. Für Node.js:

claude "Schreib einen Kafka Producer in Node.js mit kafkajs.
Er soll Bestellungs-Events an das Topic 'orders' senden,
mit ordentlichem Error-Handling und graceful shutdown"
const { Kafka } = require('kafkajs');

const kafka = new Kafka({
  clientId: 'order-service',
  brokers: ['localhost:9092'],
});

const producer = kafka.producer();

async function sendOrderEvent(order) {
  await producer.send({
    topic: 'orders',
    messages: [
      {
        key: order.id,
        value: JSON.stringify({
          orderId: order.id,
          customerId: order.customerId,
          items: order.items,
          total: order.total,
          timestamp: new Date().toISOString(),
        }),
      },
    ],
  });
}

async function run() {
  await producer.connect();
  console.log('Producer connected');

  await sendOrderEvent({
    id: 'order-001',
    customerId: 'cust-42',
    items: [{ sku: 'PROD-A', qty: 2 }],
    total: 49.99,
  });

  await producer.disconnect();
}

run().catch(async (err) => {
  console.error(err);
  await producer.disconnect();
  process.exit(1);
});

Python Producer mit confluent-kafka

Für Python-Projekte generiert Claude Code denselben Producer mit der Confluent-Bibliothek — inklusive Delivery-Callback für Produktions-Szenarien:

from confluent_kafka import Producer
import json
from datetime import datetime

producer = Producer({'bootstrap.servers': 'localhost:9092'})

def delivery_callback(err, msg):
    if err:
        print(f'Delivery failed: {err}')
    else:
        print(f'Delivered to {msg.topic()} [{msg.partition()}] @ {msg.offset()}')

def send_order_event(order: dict):
    producer.produce(
        topic='orders',
        key=order['id'],
        value=json.dumps({**order, 'timestamp': datetime.utcnow().isoformat()}),
        callback=delivery_callback,
    )
    producer.flush()

4. Consumer Groups

Consumer Groups sind das Herzstück von Kafkas Skalierungsmodell. Mehrere Instanzen derselben Anwendung können als eine Group arbeiten und Partitionen unter sich aufteilen — horizontal skalierbar, ohne Konfigurationsaufwand.

claude "Erklär den Unterschied zwischen Consumer Groups und
schreib zwei Consumer-Implementierungen: einen für Lager,
einen für Zahlung — beide lesen denselben 'orders' Stream,
aber unabhängig voneinander"
const { Kafka } = require('kafkajs');

const kafka = new Kafka({ clientId: 'warehouse', brokers: ['localhost:9092'] });

const consumer = kafka.consumer({ groupId: 'warehouse-service' });

async function run() {
  await consumer.connect();
  await consumer.subscribe({ topic: 'orders', fromBeginning: false });

  await consumer.run({
    eachMessage: async ({ topic, partition, message }) => {
      const order = JSON.parse(message.value.toString());
      console.log(`[Warehouse] Processing order ${order.orderId}`);
      // Lagerbestand prüfen, Kommissionierung starten
    },
  });
}

// payment-service nutzt groupId: 'payment-service'
// und liest dieselben Events unabhängig

Wie Consumer Groups funktionieren: Jede Partition wird genau einem Consumer innerhalb einer Group zugewiesen. Bei 3 Partitionen und 3 Instanzen bekommt jede Instanz eine Partition. Bei 5 Instanzen bleiben 2 inaktiv — die maximale Parallelität ist durch die Partitionsanzahl begrenzt.

5. Topics und Partitionen richtig konfigurieren

Die Wahl der Partitionsanzahl ist eine der wichtigsten Architekturentscheidungen. Zu wenige Partitionen begrenzen den Durchsatz, zu viele erhöhen den Overhead. Claude Code hilft bei dieser Kalkulation:

claude "Wir erwarten 10.000 Bestellungen pro Stunde mit Peak-Last
von 50.000/h. Jeder Consumer verarbeitet ca. 1.000 Messages/h.
Wie viele Partitionen brauchen wir für das orders-Topic?
Erstell auch den CLI-Befehl zur Topic-Erstellung"

Claude Code rechnet: 50.000 / 1.000 = 50 Consumer nötig, also mindestens 50 Partitionen. Mit Puffer empfiehlt es 64 (nächste 2er-Potenz) und generiert:

kafka-topics.sh --create \
  --bootstrap-server localhost:9092 \
  --topic orders \
  --partitions 64 \
  --replication-factor 3 \
  --config retention.ms=604800000 \
  --config cleanup.policy=delete

Der --replication-factor 3 sorgt für Redundanz: jede Partition liegt auf 3 Brokern, der Ausfall eines Brokers verliert keine Daten.

6. Kafka Streams für Echtzeit-Verarbeitung

Kafka Streams erlaubt komplexe Transformationen direkt im Stream: Filtern, Aggregieren, Joinen von mehreren Topics — ohne externen Stream-Prozessor. Claude Code kennt die Streams-API und generiert korrekte Topologien:

claude "Schreib eine Kafka Streams Topologie in Node.js die:
1. Orders liest
2. Nur Bestellungen über 100 Euro filtert
3. Umsatz pro Kunde aggregiert (Tumbling Window 1 Stunde)
4. Ergebnis in 'revenue-by-customer' schreibt"
const { Kafka } = require('kafkajs');

// Streams mit kafkajs erfordert manuelle State-Verwaltung
// Claude Code empfiehlt hier den Java Streams Client für
// komplexe Aggregationen — oder zeigt die Alternative:

// Mit faust-streaming (Python) oder kstreams:
// pip install kstreams

import kstreams

stream_app = kstreams.StreamApp(name="revenue-aggregator")

@stream_app.stream("orders")
async def aggregate_revenue(stream):
    async for cr in stream:
        order = cr.value
        if order['total'] > 100:
            # Aggregation in lokaler State-Map
            customer_id = order['customerId']
            revenue[customer_id] = revenue.get(customer_id, 0) + order['total']
            await producer.send('revenue-by-customer', key=customer_id,
                                value={'customerId': customer_id,
                                       'total': revenue[customer_id]})

Java vs. Python/Node für Kafka Streams: Die offizielle Kafka Streams Library ist Java-only und bietet den vollen Funktionsumfang (Windowing, Joins, Exactly-once Semantics). Für Python und Node existieren Bibliotheken mit einem Teil der Funktionalität. Claude Code zeigt dir beide Optionen und empfiehlt die jeweils passende — für produktionskritische Streams oft die Java-Variante.

7. Schema Registry und Avro

JSON im Kafka-Topic funktioniert für Prototypen — in der Produktion ist es ein Problem: kein Schema-Vertrag, keine Validierung, Breaking Changes unbemerkt. Schema Registry mit Avro löst das: jedes Event-Schema ist versioniert, Consumer und Producer validieren gegen dasselbe Schema.

claude "Erstell ein Avro-Schema für unser Order-Event und zeig,
wie Producer und Consumer mit Schema Registry kommunizieren"

Claude Code generiert das Schema und den vollständigen Code:

{
  "type": "record",
  "name": "OrderEvent",
  "namespace": "com.agentic.orders",
  "fields": [
    {"name": "orderId", "type": "string"},
    {"name": "customerId", "type": "string"},
    {"name": "total", "type": "double"},
    {"name": "timestamp", "type": {"type": "long", "logicalType": "timestamp-millis"}},
    {"name": "items", "type": {
      "type": "array",
      "items": {
        "type": "record",
        "name": "OrderItem",
        "fields": [
          {"name": "sku", "type": "string"},
          {"name": "quantity", "type": "int"},
          {"name": "price", "type": "double"}
        ]
      }
    }}
  ]
}

Mit diesem Schema lehnt die Schema Registry jede Nachricht ab, die das Format verletzt — und bei Schema-Evolutionen prüft sie automatisch die Kompatibilität. Ein neues optionales Feld ist rückwärtskompatibel, ein entferntes Pflichtfeld nicht.

8. Fehlerbehandlung: Dead Letter Queue

In verteilten Systemen gehen Messages schief: invalide Daten, externe Services down, temporäre Fehler. Die sauberste Lösung ist eine Dead Letter Queue (DLQ) — ein separates Topic, in das fehlgeschlagene Messages mit Fehlerinformation landen, statt den Consumer zu blockieren.

claude "Implementiere Dead Letter Queue für unseren Order Consumer:
bei Verarbeitungsfehler nach 3 Retries → in 'orders-dlq' schreiben
mit original Message + Fehlermeldung + Timestamp"
const { Kafka } = require('kafkajs');

const kafka = new Kafka({ clientId: 'order-consumer', brokers: ['localhost:9092'] });
const consumer = kafka.consumer({ groupId: 'order-processor' });
const producer = kafka.producer();

const MAX_RETRIES = 3;

async function processWithRetry(message, retries = 0) {
  try {
    const order = JSON.parse(message.value.toString());
    await processOrder(order);
  } catch (err) {
    if (retries < MAX_RETRIES) {
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, retries)));
      return processWithRetry(message, retries + 1);
    }
    // Nach 3 Versuchen: DLQ
    await producer.send({
      topic: 'orders-dlq',
      messages: [{
        key: message.key,
        value: JSON.stringify({
          originalMessage: message.value.toString(),
          error: err.message,
          failedAt: new Date().toISOString(),
          retries: MAX_RETRIES,
        }),
      }],
    });
    console.error(`Message sent to DLQ after ${MAX_RETRIES} retries`);
  }
}

await consumer.run({
  eachMessage: async ({ message }) => processWithRetry(message),
});

Die DLQ ermöglicht spätere Analyse und manuelles Replay ohne Datenverlust. Claude Code ergänzt auf Anfrage einen Replay-Service, der DLQ-Messages zurück ins Haupt-Topic schreibt, sobald der Fehlergrund behoben ist.

9. Monitoring mit Kafka UI

Kafka UI (im Docker-Compose-Setup auf Port 8080) bietet eine vollständige Übersicht ohne Kommandozeile: Topics und ihre Konfiguration, Partition-Verteilung, Consumer Groups mit aktuellen Offsets und Lag, und einzelne Messages lesbar anzeigen.

Claude Code hilft beim Interpretieren auffälliger Metriken. Der wichtigste Wert ist der Consumer Lag — die Differenz zwischen dem letzten produzierten und dem zuletzt verarbeiteten Offset. Steigender Lag bedeutet: der Consumer kommt nicht hinterher. Claude Code analysiert mögliche Ursachen:

claude "Unser orders Consumer Group hat einen Lag von 50.000
und er steigt weiter. Was sind die wahrscheinlichsten Ursachen
und wie diagnostiziere ich das systematisch?"
"Ein steigender Lag kann drei Ursachen haben: zu wenige Consumer-Instanzen für die Partition-Anzahl, eine langsame Verarbeitungslogik (externer API-Call, schwere DB-Query), oder ein blockierter Consumer der auf ein Lock wartet. Erste Diagnose: Consumer-Throughput messen — wie viele Messages verarbeitet eine Instanz pro Sekunde? Wenn der Wert unter dem Producer-Throughput liegt, braucht ihr mehr Instanzen oder schnellere Verarbeitung."

Diese Art Analyse — Kontext lesen, systematisch diagnostizieren, konkreten nächsten Schritt empfehlen — ist der Kern des Nutzens von Claude Code für Kafka-Systeme. Nicht nur Code generieren, sondern das System verstehen und erklären.


Weitere Artikel die auf diesem Thema aufbauen:

Claude Code Mastery — Backend, Agents, Kafka und mehr auf Deutsch

Der Kurs behandelt Event-driven Architecture, komplexe Backend-Systeme, autonome Agents und produktive Entwicklungsworkflows. Vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

Kurs · Claude Code Mastery

Event-Streaming, Agents und produktive KI-Workflows

Kafka. GraphQL. Agents. MCP. Hooks. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht