In einem Request-Response-System sind die Pfeile im Diagramm der Aufrufgraph. In einem ereignisgesteuerten System sind sie das nicht. Ein Producer schreibt ein Event in ein Topic und weiß nicht, wer es liest. Zeichnest du das naiv und verbindest jeden Service mit einer großen Kafka-Box, verbirgt das Diagramm alles, was du brauchst: welche Events es gibt, wer darauf reagiert, in welcher Reihenfolge und was passiert, wenn ein Consumer ausfällt.
Dieser Guide gibt dir eine Notation für Diagramme ereignisgesteuerter Architekturen, die den Ablauf lesbar hält, geht ein Beispiel zur Bestellverarbeitung mit Kafka durch und behandelt die Details, die beim Zeichnen gern vergessen werden: Keys, Consumer Groups, die Outbox und den Dead-Letter-Pfad.
Was das Diagramm zeigen muss
Ein ereignisgesteuertes Diagramm sollte diese Fragen ohne separates Dokument beantworten:
- Welche Events gibt es? In der Vergangenheitsform benannt, etwa
OrderPlacedoderPaymentFailed. - Wer produziert welches Event? Genau ein Owner pro Event-Typ ist der gesunde Normalfall.
- Wer konsumiert es, und als welche Consumer Group? In Kafka bekommt jede Consumer Group jedes Event, also ist jede Gruppe ein eigener Leser.
- Was bestimmt die Reihenfolge? Kafka hält die Reihenfolge nur innerhalb einer Partition ein, und der Key bestimmt die Partition.
- Was passiert bei Fehlern? Retries, Dead-Letter-Topics und wer Nachrichten erneut einspielt.
Eine Notation, die funktioniert
| Element | So zeichnest du es |
|---|---|
| Producer | Ein Service-Node mit einer gestrichelten Kante zum Topic, beschriftet mit dem Event-Typ |
| Topic | Ein eigener Node, benannt wie im Cluster (orders.v1), mit dem Key im Label |
| Consumer Group | Eine gestrichelte Kante vom Topic zum Consumer, beschriftet mit der Group-ID |
| Event vs. Command | Events in der Vergangenheitsform (OrderPlaced), Commands im Imperativ (ReserveStock) |
| Dead-Letter-Topic | Ein separater Topic-Node, gespeist von dem Consumer, der aufgegeben hat |
| Connector | Ein Node zwischen einem Topic und einem externen Speicher, etwa ein Warehouse-Sink |
| Broker-Cluster | Ein Frame um die Topics; Broker und Partitionen gehören ins Ops-Diagramm |
Verwende durchgezogene Kanten nur für synchrone Aufrufe, etwa einen HTTP-Request an einen Zahlungsanbieter. Dann sieht man auf einen Blick, welche Teile eines Ablaufs im selben Request passieren und welche später.
Das Beispiel: Bestell-Events auf Kafka
Ein Onlineshop verarbeitet Bestellungen mit Events:
- Der Service orders schreibt die Bestellung und eine Outbox-Zeile in derselben Datenbanktransaktion.
- Ein outbox relay (Debezium auf Kafka Connect) liest die Outbox über das Datenbank-Log und veröffentlicht
OrderPlacedinorders.v1, mitorder_idals Key. - Drei Consumer Groups lesen
orders.v1: payments belastet die Karte, inventory reserviert Bestand, und ein warehouse sink lädt jedes Event ins Data Warehouse. - Payments veröffentlicht
PaymentSucceededoderPaymentFailedinpayments.v1, ebenfalls mitorder_idals Key, und der Service orders konsumiert das Topic, um den Bestellstatus zu aktualisieren. - Kann inventory eine Nachricht nach drei Versuchen nicht verarbeiten, schreibt es sie nach
inventory.dlqund macht weiter.
Hier ist der gesamte Ablauf auf einem Canvas:
Diagramminhalt
- Orders
- Orders DB (orders + outbox)
- Outbox relay
- Kafka cluster
- payments.v1 · key order_id
- orders.v1 · key order_id
- inventory.dlq
- Payments
- Warehouse sink
- Inventory
- Data warehouse
- order + outbox row, one transaction
- reads WAL
- OrderPlaced
- group: payments
- group: warehouse-sink
- group: inventory
- PaymentSucceeded/Failed
- group: orders
- load
- after 3 failed attempts
Achte darauf, was das Diagramm offensichtlich macht: Niemand ruft payments auf, eine vierte Consumer Group ändert upstream nichts, und die Events einer Bestellung bleiben innerhalb jedes Topics in Reihenfolge, weil beide Topics order_id als Key verwenden (über Topics hinweg garantiert Kafka keine Reihenfolge).
Fünf Regeln für ereignisgesteuerte Diagramme
1. Topics sind Nodes, keine Pfeile
Ein Pfeil von orders zu payments mit dem Label „Kafka“ suggeriert einen Aufruf, den es nicht gibt. Zeichne das Topic als Node zwischen beiden. Es ist etwas Reales, mit einem Namen, einem Owner, einer Aufbewahrungsdauer und einem Schema.
2. Pfeile folgen den Daten
Zeichne vom Producer zum Topic und vom Topic zum Consumer, in die Richtung, in die sich die Daten bewegen. Kafka-Consumer arbeiten per Pull, aber ein Diagramm, dessen Pfeile von Consumern zu Topics zeigen, liest sich für fast alle rückwärts. Wenn die Pull-Semantik für die Diskussion wichtig ist, ergänze eine Notiz.
3. Events und Consumer Groups beschriften
Schreib den Event-Typ an die Producer-Kante und die Consumer Group an jede Consumer-Kante. Wenn jemand fragt: „Wer liest OrderPlaced noch?“, ist die Antwort die Liste der beschrifteten Kanten, die das Topic verlassen.
4. Den Key zeigen, wenn die Reihenfolge zählt
Kafka garantiert die Reihenfolge innerhalb einer Partition, und Events mit demselben Key landen in derselben Partition. Wenn sich deine Consumer darauf verlassen, OrderPlaced vor OrderCancelled zu sehen, schreib den Key ans Topic: orders.v1 · key order_id. Ein Topic ohne Key im Label provoziert genau diese Frage.
5. Den Unhappy Path zeichnen
Consumer Groups in Kafka haben keine eingebaute Dead-Letter-Queue: Dein Consumer-Code oder dein Framework entscheidet, wie oft ein Retry erfolgt und wohin eine Nachricht kommt, die sich nicht verarbeiten lässt (Sink-Connectors in Kafka Connect haben dagegen eine Einstellung für ein Dead-Letter-Topic). Zeichne diese Entscheidung. Ein Dead-Letter-Topic mit einer Kante vom Consumer und eine Notiz dazu, wer es überwacht und wie Nachrichten erneut eingespielt werden, ersparen dir einen Incident.
Die Outbox zeichnen, nicht die Abkürzung
Schreibt ein Service in zwei getrennten Schritten erst in seine Datenbank und veröffentlicht dann in Kafka, führt ein Absturz dazwischen dazu, dass ein Event verloren geht oder eines für eine Änderung veröffentlicht wird, die nie committet wurde. Das Outbox-Pattern vermeidet diesen Dual Write: Der Service schreibt das Event in derselben Transaktion in eine Outbox-Tabelle, und ein Relay veröffentlicht es.
Zeichne im Diagramm den echten Pfad: vom Service über die Datenbank und das Relay zum Topic. Ein direkter Pfeil vom Service zum Topic würde ein anderes, weniger zuverlässiges System beschreiben, und Leute treffen auf dieser Grundlage Entscheidungen.
Choreografie oder Orchestrierung
Das Beispiel nutzt Choreografie: Services reagieren auf die Events der anderen, und niemand steuert den gesamten Ablauf. Nutzt du stattdessen einen Orchestrator, also eine Workflow-Engine, die Commands wie ReserveStock sendet und auf Antworten wartet, zeichne den Orchestrator als Service in der Mitte und beschrifte seine Kanten als Commands. So oder so: Nummeriere die Schritte des Hauptablaufs an den Kanten, damit Leser die Saga der Reihe nach verfolgen können.
Den Cluster aus der logischen Sicht heraushalten
Broker, Partitionen, Replikationsfaktoren und Controller-Nodes sind für Kapazität und Betrieb wichtig, nicht für das Verständnis des Ablaufs. Leg sie auf ein separates Ops-Diagramm. In der logischen Sicht reicht ein Frame mit dem Label „Kafka cluster“ um die Topics.
In Archboard nachbauen
- Melde dich mit Google oder GitHub an und leg ein Diagramm an.
- Füge die Services als Container-Nodes aus dem C4-Modell-Pack hinzu und die orders-Datenbank aus dem Daten-Pack (Postgres).
- Füge einen Basis-Rahmen mit dem Label
Kafka clusterhinzu und setze Thema-Nodes aus dem Daten-Pack hinein. - Füge das Outbox-Relay und den Warehouse-Sink als Container mit
Kafka Connectim Feld Technologie hinzu, dazu einen Datenlager-Node aus dem Daten-Pack. - Verbinde die Ports, stell die Event-Kanten auf gestrichelt und beschrifte sie mit Event-Typen und Consumer Groups.
- Öffne das Diagramm für das Review im Präsentationsmodus, teile den Live-Link oder exportiere SVG und PNG.
Das Beispiel nutzt 11 Diagramm-Nodes und bleibt damit innerhalb der 15 des Free-Plans. Ein weiteres Streaming-Layout findest du unter /examples/streaming-data-platform, die synchrone Seite eines ähnlichen Shops im Guide zum Microservices-Architekturdiagramm.
FAQ
Soll ich Partitionen zeichnen? Nicht in der logischen Sicht. Schreib die Partitionsanzahl ins Topic-Label, wenn sie wichtig ist, und zeichne Partitionen und Broker in einem Ops-Diagramm.
Wie unterscheidet sich im Diagramm eine Queue von einem Topic? In einer Queue geht jede Nachricht an genau einen Consumer; in einem Kafka-Topic bekommt jede Consumer Group jede Nachricht. Nutze für Queues wie SQS oder RabbitMQ einen Warteschlangen-Node und gib an, auf welche Semantik du dich verlässt.
Wohin gehören Event-Schemas? In eine Notiz oder ins Topic-Label, mit einem Link zur Schema Registry oder zum Repository, dem das Schema gehört. Das Diagramm sollte zeigen, dass es einen Vertrag gibt und wo er liegt.
Wie zeige ich Replays?
Schreib die Retention ans Topic (zum Beispiel retention 7d) und ergänze eine Notiz dazu, wie eine Consumer Group ihre Offsets zurücksetzt. Replay ist ein Designmerkmal; mach es sichtbar.
Brauche ich ein Diagramm pro Event? Nein. Ein Diagramm pro Geschäftsprozess funktioniert gut. Führ die Events an den Kanten auf und pflege für die vollständige Liste einen Event-Katalog.