Table of Contents
Ereignisgesteuerte Architekturen beruhen auf einem zuverlässigen, konsistenten Datenfluss zwischen Produzenten und Verbrauchern. Mit der Entwicklung von Systemen ändert sich zwangsläufig die Struktur von Ereignisdaten - ihr Schema -. Neue Felder werden hinzugefügt, alte Felder werden veraltet und manchmal verschieben sich ganze Datenmodelle. Ohne eine bewusste Strategie für das Management dieser Änderungen können ereignisgesteuerte Systeme spröde werden, was Deserialisierungsfehler, Datenverlust oder stille Fehldarstellung von Informationen auslöst. Ereignisversionierung und Schemaentwicklung sind die Disziplinen, die diese Komplexität überschaubar halten und es Teams ermöglichen, unabhängig zu iterieren und gleichzeitig die Kompatibilität zwischen Diensten zu gewährleisten. Dieser Artikel bietet eine produktionsgeprüfte Anleitung zum Entwerfen und Implementieren von Versionierungs- und Schemaentwicklungsstrategien, die mit Ihrem System skalierbar sind.
Verstehen von Event Versioning
Die Ereignisversionierung ist die Praxis, verschiedene Versionen eines Ereignisschemas zu identifizieren und zu verfolgen, so dass Produzenten und Konsumenten in verschiedenen Evolutionsstadien koexistieren können. Das Hauptziel besteht darin, sicherzustellen, dass Ereignisse korrekt interpretiert werden können, unabhängig davon, wann sie produziert wurden oder von welcher Version eines Produzenten. Dies erfordert sowohl rückwärtskompatibilität (neue Konsumenten können Ereignisse lesen, die von alten Produzenten produziert wurden) als auch vorwärtskompatibilität (alte Konsumenten können Ereignisse lesen, die von neuen Produzenten produziert wurden).
Versionierung kann auf verschiedenen Ebenen implementiert werden:
- Schema-Versionierung – Die Schemadefinition selbst trägt eine Versionskennung (z. B. ). Dies ist der expliziteste Ansatz und funktioniert gut mit Schema-Registern.
- Payload-Versionierung – Die Ereignis-Payload enthält ein Versionsfeld (z. B. ), das dem Verbraucher mitteilt, welches Schema er für die Deserialisierung verwenden soll.
- Metadatenversionierung – Versionsinformationen werden in Nachrichten-Headern oder Envelope-Metadaten gespeichert, die von der Nutzlast getrennt sind.
Jeder Ansatz hat Kompromisse. Schema-Versionierung zentralisiert die Schemaverwaltung und erleichtert Kompatibilitätsprüfungen, erfordert aber oft Laufzeit-Schema-Registrierungs-Lookups. Payload-Versionierung ist einfach zu implementieren und funktioniert in Systemen ohne Registry, kann aber die Nutzlast aufblähen und erfordert einen sorgfältigen Umgang mit Versionsfeldern. Metadaten-Versionierung hält das Nutzlast-Schema sauber, erhöht aber die Komplexität der anfänglichen Parsing-Logik des Verbrauchers. In der Praxis kombinieren viele Teams Schema-Register mit metadatenbasierter Versionierung, um das Beste aus beiden Welten zu bekommen.
Strategien für Schema Evolution
Schema-Evolution ist die Reihe von Regeln und Praktiken, die regeln, wie sich Schemas im Laufe der Zeit ändern, während die Kompatibilität gewahrt bleibt.
Schemavalidierung
Die beliebtesten Optionen sind JSON Schema, Apache Avro und Protocol Buffers (Protobuf). Diese Sprachen bieten integrierte Mechanismen für die Evolution, wie Standardwerte, optionale Felder und Kompatibilitätsmodi. Die Validierung stellt sicher, dass jedes erzeugte Ereignis die erwartete Struktur erfüllt, wodurch die Wahrscheinlichkeit einer Beschädigung stiller Daten nachgelagert reduziert wird.
Rückwärtskompatibilität
Eine Schemaänderung ist rückwärtskompatibel, wenn ein Verbraucher, der für das neue Schema geschrieben wurde, noch Ereignisse lesen kann, die durch das alte Schema erzeugt wurden.
- Optionale Felder hinzufügen – Neue Felder sollten optional mit sinnvollen Standardeinstellungen (z. B. oder einem Nullwert) sein.
- Neue enum-Werte hinzufügen – Neue enum-Werte können hinzugefügt werden, solange sie nicht die bestehende Logik brechen.
- Felder ungültig machen – Das Ändern eines erforderlichen Feldes in optional ist rückwärtskompatibel; das Gegenteil ist eine brechende Änderung.
- Mit Typ-Promotions – Ein Feld zu erweitern (z.B. → , ] ist oft sicher, aber Verengung kann Datenverlust verursachen.
Kompatibilität nach vorn
Die Vorwärtskompatibilität stellt sicher, dass ein älterer Verbraucher Ereignisse lesen kann, die von einem neueren Hersteller produziert wurden. Dies ist schwieriger zu erreichen, da der Verbraucher nicht über Bereiche Bescheid weiß, für die er nicht programmiert wurde.
- Tolerante Leser – Verbraucher sollten unbekannte Felder während der Deserialisierung ignorieren. Die meisten Schemaformate unterstützen dies: Avro’s Option, Protobuf’s und unbekannte Felderhaltung und JSON Schema’s .
- Standardwerte für neue Felder – Hersteller können neue Felder mit Standardwerten füllen, wenn der Verbraucher sie nicht verwenden kann, aber dies ist wirklich ein Problem der Rückwärtskompatibilität.
- Vermeidung struktureller Veränderungen – Das Umbenennen von Feldern, Ändern von Typen oder Reorganisieren verschachtelter Strukturen unterbricht typischerweise die Kompatibilität nach vorne.
Versionierung in Metadaten
Die Einbettung von Versionsinformationen in Nachrichten-Header oder einen Umschlags-Wrapper entkoppelt die Version vom Nutzdatenschema. Ein gängiges Muster ist die Verwendung eines -Headers in Apache Kafka-Headern oder eines -Feldes in einem Umschlagobjekt. Dieser Ansatz ermöglicht es Herstellern und Verbrauchern, die Schemaauflösung auf der Anwendungsebene zu handhaben, ohne das Nutzdatenschema selbst zu ändern. Es legt jedoch die Verantwortung auf den Verbraucher, die richtige Schemaversion vor der Deserialisierung abzurufen, typischerweise unter Verwendung einer Schemaregistrierung.
Schema-Register
Eine Schemaregistrierung ist ein zentralisierter Dienst, der Schemas über mehrere Versionen hinweg speichert und validiert. Er erzwingt Kompatibilitätsregeln (z. B. rückwärts, vorwärts, vollständig oder nicht) und bietet eine Möglichkeit für Verbraucher, das Schema abzurufen, das zur Deserialisierung eines Ereignisses erforderlich ist. Confluent Schema Registry ist die am häufigsten verwendete für Kafka-basierte Systeme, aber es gibt Open-Source-Alternativen wie Apicurio Registry und Azure Schema Registry. Die Verwendung einer Registry ermöglicht es, Kompatibilitätsprüfungen in CI / CD-Pipelines zu automatisieren und verhindert, dass inkompatible Schemas in die Produktion eingeführt werden.
Versionierung in der Praxis umsetzen
Um von der Theorie zur Umsetzung zu gelangen, müssen konkrete Entscheidungen über Serialisierungsformate, Werkzeuge und Prozesse getroffen werden. Folgende Praktiken haben sich in produktionsereignisgesteuerten Hochdurchsatzsystemen bewährt.
Wählen Sie ein Serialisierungsformat
Das Serialisierungsformat bestimmt, wie Schemata definiert werden, wie sie sich entwickeln und welche Kompatibilität garantiert wird.
- Apache Avro – Entwickelt für Schemaentwicklung. Unterstützt Rückwärts-, Vorwärts- und Vollkompatibilitätsmodi. Verwendet ein kompaktes Binärformat mit starker Typisierung. Gut integriert in Confluent Schema Registry. Am besten für Java-zentrierte Kafka-Ökosysteme.
- Protokollpuffer (Protobuf) – unterstützt auch die Evolution über Feldnummern und optionale Felder. Effizienter als Avro für einige Workloads. Funktioniert gut in polyglotten Systemen mit gRPC. Kompatibilitätsregeln sind weniger eingebaut, können aber mit Tools von Drittanbietern wie Buf durchgesetzt werden.
- JSON Schema – Menschlich lesbar, weithin unterstützt und einfach zu debuggen. Keine eingebaute binäre Serialisierung; typischerweise mit JSON verwendet. Evolution wird über die Spezifikation verwaltet (z. B. , ).
In vielen Unternehmen ist die Auswahl bereits durch die bestehende Infrastruktur eingeschränkt. Wenn Sie neu anfangen, bietet Avro die ausgereifteste Toolchain für die Entwicklung von Schemata für das Event-Streaming, während Protobuf ein starker Konkurrent für die Kommunikation mit Microservices ist.
Beibehaltung von Kompatibilitätsmatrizen
Mit zunehmender Anzahl von Schemas und Versionen ist es wichtig zu dokumentieren, welche Versionen mit welchen kompatibel sind. Eine Kompatibilitätsmatrix bildet die Herstellerschemaversionen den Verbraucherschemaversionen zu, wobei bekannte Inkompatibilitäten hervorgehoben werden. Diese Matrix kann als YAML-Datei in Ihrem Schema-Repository gepflegt oder automatisch von einer Schema-Registrierung generiert werden. Sie dient sowohl als Kommunikationswerkzeug für Teams als auch als Quelle der Wahrheit für automatisierte Tests.
Zum Beispiel könnte eine Matrix aufzeichnen, dass rückwärtskompatibel mit ist, aber nicht vorwärtskompatibel ist, was bedeutet, dass alte Verbraucher brechen, wenn sie v2-Ereignisse erhalten.
Automatisierte Kompatibilitätsprüfung
Manuelle Prüfungen auf Schemakompatibilität werden schnell unüberschaubar. Integrieren Sie Schemavalidierung und Kompatibilitätsprüfungen in Ihre CI/CD-Pipeline. Jedes Mal, wenn ein Hersteller ein Schema ändert, sollte die Pipeline:
- Registrieren Sie das neue Schema mit einem angegebenen Kompatibilitätsmodus gegen die Schemaregistrierung.
- Wenn die Registrierung fehlschlägt, brechen Sie den Build ab und fordern Sie das Team auf, das Schema zu beheben (oder die Ereignisversion explizit zu stoßen).
- Führen Sie Integrationstests mit tatsächlichen Verbrauchern durch, die das neue Schema ausüben, um Laufzeitprobleme zu erkennen.
- Wenn dies erfolgreich ist, veröffentlichen Sie die neue Schemaversion zusammen mit einem Changelog-Eintrag.
Tools wie Confluents Maven-Plugin oder benutzerdefinierte Shell-Skripte (und jetzt GitHub-Aktionen) können dies automatisieren. Für Protobuf bietet die Buf-CLI einen robusten -Befehl, der Kompatibilitätsregeln durchsetzt.
Kommunikation und Dokumentation
Schemaänderungen sind implizite API-Verträge. Sie sollten wie jede andere API-Änderung kommuniziert werden. Führen Sie ein Changelog für jeden Ereignistyp, wobei Sie angeben, was sich geändert hat, warum und welche Kompatibilitätsgarantien gelten. Verwenden Sie ein Schema-Dokumentationstool (z. B. Backstage, eine generierte Site aus Ihrer Schema-Registrierung), damit Teams verfügbare Schemas und deren Verlauf durchsuchen können. Wenn eine Unterbrechung der Änderung unvermeidlich ist, geben Sie sie im Voraus bekannt, stellen Sie ein Migrationsfenster bereit und stellen Sie sicher, dass alle Verbraucher vor dem Einsatz des neuen Schemas aktualisiert werden.
Umgang mit Breaking Changes
Trotz bester Bemühungen, sie zu vermeiden, müssen manchmal kaputte Änderungen auftreten (z. B. Umbenennen eines Feldes, Ändern eines Datentyps, Restrukturieren von verschachtelten Objekten).
- Versioned topics – Erstellen von Events zu einem neuen Thema (z. B. ), während alte Verbraucher weiterhin vom alten Thema lesen.
- Event Version Bumps – Behalte das gleiche Thema bei, aber erhöhe die Hauptversionsnummer des Ereignisses. Verbraucher müssen die Version überprüfen und entscheiden, wie sie deserialisieren. Dies vermeidet die Verbreitung von Themen, fügt aber Verzweigungslogik bei Verbrauchern hinzu.
- Dual schreibt – Für eine Übergangszeit gibt der Produzent sowohl das alte als auch das neue Ereignisformat aus. Dies wird oft als Sprungbrett verwendet, während die Verbraucher migriert werden. Es verdoppelt den Traffic und erhöht die Komplexität, daher sollte es vorübergehend sein.
Welchen Weg Sie auch wählen, verbinden Sie die bahnbrechende Änderung immer mit einer klaren Abwertungsrichtlinie und einem überwachten Rollout.
Fortgeschrittene Überlegungen
Wenn sich Ereignisversionierung und Schemaentwicklung mit Ereignisbeschaffung, polyglotten Umgebungen oder spezialisierten Ereignisspeichern überschneiden, entstehen zusätzliche Nuancen.
Event Sourcing und Schema Evolution
In ereignisbasierten Systemen sind Ereignisse die Quelle der Wahrheit und werden nie gelöscht oder verändert. Schema-Evolution wird zu einem kritischen Design-Problem, weil jedes vergangene Ereignis für immer interpretierbar bleiben muss. Die empfohlene Praxis ist, Ereignisse in einem Format zu speichern, das die Schema-Evolution nativ unterstützt (z. B. Avro oder Protobuf mit einer Schema-Registrierung) und immer Felder mit Standardeinstellungen hinzuzufügen, anstatt bestehende zu modifizieren. Wenn eine grundlegende Umstrukturierung erforderlich ist, sollten Sie einen neuen Ereignistyp erstellen und über eine Projektion migrieren.
Versionierung in polyglotten Umgebungen
Wenn Produzenten und Konsumenten in verschiedenen Sprachen geschrieben sind, müssen Sie sicherstellen, dass das Serialisierungsformat und die Schemadefinition in allen Sprachen konsistent sind. Avro und Protobuf haben beide eine robuste Codegenerierung für viele Sprachen, aber jede Sprache kann unbekannte Felder oder Standardwerte leicht unterschiedlich behandeln. Testen Sie die Kompatibilität zwischen den Sprachen frühzeitig in der Entwicklung. Verwenden Sie eine Schemaregistrierung, die sprachunabhängige Serialisierer bereitstellt (z. B. Confluents REST-Proxy für Avro), um eine Duplizierung der Schemaauflösungslogik zu vermeiden.
Versionierung mit Event Stores
Systeme wie EventStoreDB oder Apache Kafka (als Event Store genutzt) haben oft eigene Mechanismen für das Schemamanagement. EventStoreDB unterstützt Ereignistypen und -projektionen, aber die Schemaentwicklung liegt weiterhin in Ihrer Verantwortung. Bei Kafka ist die Schemaregistrierung das primäre Werkzeug. Wenn Sie Kafka jedoch als Langzeit-Event Store verwenden, sollten Sie eine Aufbewahrungsrichtlinie hinzufügen, die alte Ereignisse erst verdichtet oder löscht, nachdem alle Verbraucher in ein neues Schema migriert wurden. Andernfalls besteht die Gefahr, dass Sie Ereignisse mit einem veralteten Schema haben, das kein Verbraucher lesen kann.
Schlussfolgerung
Event-Versionierung und Schema-Evolution sind in keinem Event-getriebenen System optional, das erwartet, über einen einzigen Release-Zyklus hinaus zu leben. Durch die Annahme von Schema-Registern, die Auswahl des richtigen Serialisierungsformats und die Automatisierung von Kompatibilitätsprüfungen können Teams ihre Datenschemata mit Zuversicht weiterentwickeln. Rückwärts- und Vorwärtskompatibilität schützen die Verbraucher vor unerwarteten Ausfällen, während klare Kommunikations- und Abwertungsrichtlinien alle in Einklang halten. Die widerstandsfähigsten Systeme behandeln Schemaänderungen als erstklassige API-Änderungen, planen sie vom ersten Tag an. Die Investition in diese Praktiken zahlt sich jedes Mal aus, wenn ein Dienst aktualisiert wird, ein neuer Verbraucher hinzugefügt wird oder ein Legacy-Ereignis wiederholt wird - so dass Ihre Ereignisströme eine zuverlässige Grundlage für Ihre Architektur bleiben.