Event-Driven Architecture: Kafka, Streaming & CEP

In modernen Architekturen rückt die Verarbeitung von kontinuierlichen Datenströmen (“Data in Motion”) immer mehr in den Fokus. Dieser Post fasst die wichtigsten technischen Konzepte, Abgrenzungen und Frameworks rund um Event Streaming und Complex Event Processing (CEP) zusammen.


1. Message vs. Event

Obwohl die Begriffe oft synonym verwendet werden, gibt es in der Architektur einen fundamentalen Unterschied bezüglich der Absicht des Senders:

EigenschaftNachricht (Message)Event
CharakterEin Befehl oder eine Anfrage (“Mach etwas für mich”).Ein historischer Fakt (“Etwas ist passiert”).
ErwartungshaltungDer Sender erwartet, dass die Nachricht von einem spezifischen System verarbeitet wird.Dem Sender ist völlig egal, wer oder wie viele das Event lesen. Er teilt nur mit, dass es passiert ist.
VeränderlichkeitKann theoretisch in Pipelines modifiziert werden.Immutable (Unveränderlich): Da es in der Vergangenheit passiert ist, kann es niemals geändert werden.
BeispielCreateInvoiceMessageCustomerArrivedEvent

2. “Topic” ist nicht gleich “Topic”: ActiveMQ vs. Kafka

Sowohl klassische Message Broker als auch Event Streaming Plattformen verwenden den Begriff “Topic”. Die Architektur dahinter ist jedoch grundverschieden:

📨 ActiveMQ Topics (Klassisches Publish/Subscribe)

  • Flüchtig (Transient): Der Broker leitet eine Nachricht sofort an alle in diesem Moment aktiven Konsumenten weiter.
  • Weg ist weg: Sobald verteilt, wird die Nachricht gelöscht. Wer offline war, verpasst sie (es sei denn, man nutzt spezifische Mechanismen wie den Durable Subscriber, siehe dazu auch meinen separaten Post zu MOM & ESB).
  • Push-Prinzip: Der Broker drückt die Nachrichten aktiv zu den Empfängern.

📚 Kafka Topics (Event Streaming / Log-basiert)

  • Dauerhaft (Persistent): Ein Kafka-Topic ist kein Verteiler, sondern ein dauerhaftes Protokoll auf einer Festplatte (Append-only Log).
  • Events bleiben gespeichert: Auch nachdem sie gelesen wurden, bleiben Events erhalten (z.B. für 7 Tage oder für immer).
  • Pull-Prinzip & Zeitreisen: Der Konsument zieht sich die Events selbst und merkt sich seinen Fortschritt (“Offset”). Bei einem Ausfall liest er einfach ab dem letzten Offset weiter. Auch das komplette Neu-Abspielen der Historie (“Zeitreise”) ist möglich.

Zusammenfassung: In ActiveMQ schickst du Nachrichten für die sofortige Verteilung (Sicherer Transport). In Kafka schreibst du Events in ein Log, um eine historische Wahrheit festzuhalten (Speichern & kontinuierliches Verarbeiten).


3. Klassisches Enterprise Messaging vs. Event Processing

Klassisches Messaging: Der Broker (z.B. ActiveMQ) ist ein “dummes Rohr” (Dumb Pipe) für schlaue Applikationen (Smart Endpoints). Er schiebt Daten isoliert von A nach B. Die Logik liegt rein in den Applikationen.

Event Processing: Hier werden nicht einzelne Nachrichten isoliert betrachtet, sondern kontinuierliche Datenströme (Data in Motion). Ein Event-Processing-System filtert, transformiert oder aggregiert den Strom während er fliesst, meist bevor er überhaupt in einer Datenbank landet.


4. Die 3 logischen Schichten einer EDA

Eine Event-Driven Architecture (EDA) lässt sich formell in drei strikt getrennte Schichten unterteilen:

  1. Ereignisquellen (Event Sources): Nicht nur Sensoren, sondern jede Entität, die eine Zustandsänderung erkennt und meldet (z.B. IoT-Türsensoren, Klick-Streams auf einer Website oder Microservices, die ein Datenbank-Update melden).
  2. Ereignisverarbeitung (Event Processing): Hier passiert die “Magie” der Mustererkennung. Diese Schicht nimmt den Strom der rohen Events auf, filtert sie und wendet ein Regelwerk an.
  3. Ereignisbehandlung (Event Handling): Die Reaktion auf die Ereignisverarbeitung. Das nachgelagerte System (z.B. ein Dashboard oder ein Polizei-Webservice) weiss nichts von abstrakten Mustern. Es konsumiert lediglich die in Schicht 2 neu erstellten, komplexen Events und führt daraufhin seine Geschäftslogik aus (z.B. Sirene aktivieren).

5. EPA und EPN: Die Anatomie der Verarbeitung

Innerhalb der Ereignisverarbeitung (Schicht 2) arbeiten spezialisierte Softwaremodule:

  • Event Processing Agent (EPA): Ein EPA ist die kleinste logische Einheit. Er hat eine spezifische Aufgabe. Ein EPA kann trivial sein (z.B. “Filtere alle Events unter 20 Grad raus”) oder hochkomplex (z.B. CEP-Mustererkennung über Zeitfenster).
  • Event Processing Network (EPN): Komplexe Logik wird nie in eine gigantische Mega-Regel gepackt. Stattdessen verkettet man mehrere EPAs über Event-Kanäle zu einer Pipeline (dem EPN).
    • Beispiel-Pipeline: EPA 1 filtert unwichtige Events aus -> übergibt an EPA 2. EPA 2 reichert die Events mit Datenbank-Infos an -> übergibt an EPA 3 zur Mustererkennung.

6. Event Algebra & Verarbeitungsmuster

Um Muster im EPN zu erkennen, nutzt man eine sogenannte Event Algebra (z.B. Sequenzen A -> B oder boolesche Operatoren A AND B). Da Datenströme endlos sind, bedient man sich sogenannter Sliding Windows (Gleitfenster), um den Datenstrom in verdaubare Häppchen zu unterteilen (z.B. Zeitfenster: “Die letzten 5 Minuten” oder Längenfenster: “Die letzten 10 Events”).

Man unterscheidet in der Verarbeitungstiefe:

Single Event Processing

Jedes Event wird einzeln und isoliert betrachtet.

  • Logik: WENN Event-Wert > X DANN tue Y.
  • Beispiel: Ein Fenstersensor meldet WindowBrokenEvent. Das System reagiert isoliert auf dieses eine Event und schlägt Alarm.

Complex Event Processing (CEP)

Betrachtet Muster (Patterns) über viele Events hinweg, nutzt Sliding Windows und reichert Daten an (Content Enrichment). Das Ziel ist die Synthese: Aus vielen unbedeutenden “Low-Level-Events” wird ein neues, bedeutungsvolles “Complex Event” abgeleitet.

Die 3 Paradebeispiele für CEP (Smart Home Security)

  1. Räumlich-zeitliche Häufung: Verknüpfung mehrerer unabhängiger Events in einem Bereich.
    • Muster: Wenn 3 Rauchmelder auf demselben Stockwerk innerhalb von 2 Minuten Alarm schlagen.
    • Aktion: Generiere FireAlertEvent (Feueralarm auslösen).
  2. Zeitliche Eskalation (Sequenz): Wiederholtes Auftreten desselben Events.
    • Muster: Wenn das Keypad an der Haustür innerhalb von 5 Minuten dreimal hintereinander einen falschen PIN-Code meldet (WrongPinEvent).
    • Aktion: Generiere BruteForceAlertEvent (Sicherheitsdienst informieren).
  3. Das Absence Pattern (Kausal-zeitlich): Das mächtigste Werkzeug von CEP! Wir reagieren auf etwas, das nicht passiert ist. Klassisches Single Event Processing kann das nicht.
    • Muster: Ein Türsensor meldet DoorOpenedEvent. Wenn innerhalb eines Zeitfensters von 60 Sekunden kein PinCodeEnteredEvent am Keypad folgt.
    • Aktion: Das EPA folgert daraus einen Einbruch und generiert das komplexe BurglaryAlertEvent. Die nachgelagerte Ereignisbehandlung empfängt dieses synthetisierte Event und aktiviert die Sirene.

7. Frameworks für Stream Processing im Vergleich

Um diese Datenströme im grossen Stil zu verarbeiten, gibt es verschiedene Ansätze und Frameworks:

FrameworkArchitektur-AnsatzLatenzBesonderheit / Use-Case
Apache Spark (Streaming)Micro-Batching: Sammelt Events (z.B. für 1 Sekunde) und verarbeitet sie als Mini-Stapel.Hoch (Sekunden)Extrem hoher Durchsatz. Unified Engine (Super für Kombination mit Machine Learning / Batching).
Apache StormNative Streaming (Pionier): Event-by-Event Verarbeitung.Sehr gering (Sub-ms)Einer der Pioniere für echtes Streaming, gilt architektonisch heute jedoch oft als veraltet.
Apache FlinkStateful True Streaming: Event-by-Event mit exzellentem Zustands-Management.Sehr geringDer aktuelle Goldstandard. Meisterhaft im Sortieren von Events nach Entstehungszeitpunkt (Event Time) statt Eintreffzeitpunkt, starkes Handling von “Late Data”.
Kafka StreamsLightweight Library: Reine Java/Spring-Boot Bibliothek.GeringKein riesiger Cluster nötig! Ideal, wenn Kafka ohnehin als Backbone läuft. Skaliert einfach über das Starten weiterer App-Instanzen.

💡 Best Practice: Event Schema Registry

In grossen, produktiven Systemen (Microservice-Architekturen) werden Event-Typen nicht einfach als rohe Strings verschickt. Man nutzt typischerweise eine Schema Registry und Formate wie Apache Avro. Jedes Event muss dort einen definierten Typ (z.B. "type": "BurglaryAlertEvent") deklarieren, was Datensicherheit garantiert und Versionierung (Schema Evolution) ermöglicht.