Versionierung

Die Notwendigkeit, Services in mehreren Versionen anbieten zu können, ist bedingt durch die Vielzahl an Service-Nutzern, die bei Änderung an einem Service nicht alle zeitgleich auf die neue Version eines Service umschalten können. Daher ist es notwendig, dass in einem, wenn möglich, kurz zu haltenden Übergangszeitraum mehrere Versionen eines Service parallel betrieben werden können.

Die Versionierung wird auf der Ebene von Services, nicht Service-Operationen ausgeführt, da diese Ebene von ihrer Granularität zu den üblichen fachlichen Änderungen passt.

Es kann vorkommen, dass in einem Systemrelease neue Versionen von mehreren Services ausgeliefert werden.

Eine Version beschreibt die evolutionäre Weiterentwicklung desselben fachlichen Konzepts. Wird ein anderes fachliches Konzept umgesetzt, ist keine Versionierung, sondern eine neue Schnittstelle erforderlich.

1. Abwärtskompatibilität

Als Abwärtskompatibilität wird die Verwendbarkeit bzw. Kompatibilität neuerer oder erweiterter Versionen eines Services bzw. einer Nachricht oder eines Events zu den Anwendungsbedingungen einer früheren Version bezeichnet. Diese liegt vor, wenn bestehende Kommunikationsteilnehmer eine neue Version eines Services, einer Nachricht oder eines Events ohne Anpassung ihres Codes oder ihrer Konfiguration weiterhin korrekt nutzen können

Diese Definition basiert auf dem Wikipedia-Artikel zur Abwärtskompatibilität.

Abwärtskompatibilität umfasst sowohl technische als auch fachliche Aspekte. Technische Kompatibilität liegt vor, wenn die Struktur und Datentypen weiterhin verarbeitet werden können. Fachliche Kompatibilität liegt vor, wenn die Semantik der Daten unverändert bleibt. Eine Änderung kann technisch kompatibel, aber fachlich inkompatibel sein und stellt in diesem Fall einen Breaking Change dar. Abwärtskompatibilität ermöglicht Übergangszeiträume, in denen sich die Kommunikationsteilnehmer schrittweise an die Änderungen anpassen können, ohne dass es zu Unterbrechungen oder Fehlern in der Kommunikation kommt.

Die Verantwortung für Änderungen und damit für die Abwärtskompatibilität liegt in der Regel beim Provider von Services bzw. beim Producer/Publisher von Nachrichten und Events, da diese die Schnittstelle bzw. das Format der Nachrichten und Events definieren und somit für die Consumer festlegen. Ausnahmen bilden Szenarien, in denen ein Consumer viele Producer/Publisher orchestriert und aus diesem Grund das Format der Nachrichten oder Events vorgibt, zum Beispiel im Bereich Telemetrie oder Protokollierung.

Inkompatible Änderungen (englisch: breaking changes) sind dagegen Änderungen an der Schnittstelle eines Services bzw. am Format einer Nachricht oder eines Events, welche die Anwendungsbedingungen einer früheren Version verletzen. Sie führen zu Fehlern oder Unterbrechungen in der Kommunikation, wenn die Kommunikationsteilnehmer nicht gleichzeitig auf die neue Version wechseln können und müssen daher genau geplant und kommuniziert werden.

1.1. Abwärtskompatible Änderungen

Für die Gewährleistung von Abwärtskompatibilität wird ein tolerantes Verhalten der Kommunikationsteilnehmer vorausgesetzt:

  • Clients bzw. Consumer müssen unbekannte Felder ignorieren können.

  • Server bzw. Producer dürfen bestehende Felder nicht entfernen oder in ihrer Bedeutung verändern.

Bei asynchroner Kommunikation ist Abwärtskompatibilität dann gegeben, wenn bestehende Consumer Nachrichten oder Events weiterhin verarbeiten können, ohne dass Fehlverhalten entsteht.

Diese Grundsätze gelten auch für synchrone Kommunikation. Bei asynchroner Kommunikation ist ein tolerantes Verhalten der Consumer zwingend erforderlich, da Producer und Consumer zeitlich und technisch entkoppelt sind.

Bei synchroner Kommunikation wird Abwärtskompatibilität dadurch erreicht, dass bestehende Service-Nutzende neue Versionen ohne Anpassung weiterhin korrekt nutzen können. Dies setzt insbesondere voraus, dass zusätzliche Felder die Verarbeitung bestehender Clients nicht beeinträchtigen. Die Schnittstelle muss so gestaltet sein, dass bestehende Clients weiterhin funktionieren.

Die folgenden Beispiele stellen häufig vorkommende, abwärtskompatible Änderungen dar.

Änderungstyp Auswirkung auf die Kommunikation

Optionales Feld hinzufügen

Die Kommunikationsteilnehmer können das Feld bei Bedarf setzen bzw. berücksichtigen.

Neues Feld mit Standardwert hinzufügen

Die Kommunikationsteilnehmer können das Feld bei Bedarf mit einem eigenen Wert belegen bzw. berücksichtigen.

Optionale neue Operation / Nachricht / Event hinzufügen

Die Kommunikationsteilnehmer können die Operation bei Bedarf aufrufen, bzw. die Nachricht oder das Event produzieren oder konsumieren.

Zusätzliche Response-Felder

Die Kommunikationsteilnehmer können zusätzliche Felder ignorieren, sofern sie diese nicht auswerten.

1.2. Inkompatible Änderungen

Die folgenden Beispiele stellen häufig vorkommende, inkompatible Änderungen (Breaking Changes) dar.

Änderungstyp Auswirkung auf die Kommunikation

Neues Pflichtfeld hinzufügen

Die Kommunikationsteilnehmer müssen das neue Feld verwenden. Eine Übergangsphase mit einer frühzeitigen Einführung des Feldes als optional kann die Auswirkungen dieses Breaking Changes mindern.

Feld umbenennen / entfernen

Die Kommunikationsteilnehmer können Services nicht mehr nutzen oder keine Nachrichten / Event mehr produzieren oder konsumieren.

Flexible Validierungen und Verarbeitungslogiken können die Auswirkungen eines solchen Breaking Changes mindern.

Typ oder Semantik eines Felds ändern

Werte werden von den Kommunikationsteilnehmern falsch interpretiert oder führen zu Konvertierungsfehlern, auch wenn die Struktur gleich bleibt. Diese Änderungen sollten vermieden werden, da sie schwer zu kommunizieren und zu behandeln sind.

Stattdessen sollte ein neues Feld eingeführt und das alte Feld zu einem späteren Zeitpunkt entfernt werden.

Änderung des Fehlerverhaltens oder von Statuscodes

Die Kommunikationsteilnehmer können auf unerwartete Fehler reagieren oder bestehende Fehlerbehandlungen greifen nicht mehr.

2. Entscheidungsmodell zur Versionierung

Aufbauend auf der Definition der Abwärtskompatibilität legt die Referenzarchitektur ein verbindliches Entscheidungsmodell für die Versionierung von Services, Nachrichten und Events fest.

Das Entscheidungsmodell beantwortet, wann:

  • eine bestehende Schnittstelle erweitert werden sollte,

  • eine neue Version der Schnittstelle bereitzustellen ist,

  • ein neuer Kanal erforderlich ist, oder

  • ein neues Backend sinnvoll ist.

Die Entscheidung über die Notwendigkeit einer neuen Version ist von ihrer technischen Umsetzung zu trennen. Eine neue Service-Version bedeutet nicht zwingend eine neue Serviceschnittstelle oder ein neues Backend. Die Einführung einer neuen Service-Version erzwingt also keine bestimmte technische Realisierungsform. Sie kann beispielsweise innerhalb derselben Serviceschicht und desselben Backends umgesetzt werden. In der Regel wird eine neue Service-Version als eigene Serviceschnittstelle innerhalb derselben Serviceschicht realisiert.

2.1. Entscheidungsregeln

2.1.1. Abwärtskompatible Änderungen

Ist eine Änderung abwärtskompatibel, so wird die bestehende Schnittstelle erweitert. Dies bedeutet:

  • keine neue Service-Version,

  • kein neuer Kanal,

  • kein neues Backend.

Abwärtskompatible Änderungen sollen innerhalb derselben Version weiterentwickelt werden, um unnötige Parallelität und Versionsvielfalt zu vermeiden.

2.1.2. Nicht abwärtskompatible Änderungen an synchronen Services

Ist eine Änderung an einem synchronen Service nicht abwärtskompatibel, so ist eine neue Service-Version bereitzustellen. Die bestehende Version bleibt für einen Übergangszeitraum parallel verfügbar, sofern bestehende Service-Nutzende nicht zeitgleich migriert werden können.

2.1.3. Nicht abwärtskompatible Änderungen an Nachrichten und Events

Ist eine Änderung an einer Nachricht oder einem Event nicht abwärtskompatibel, so ist eine neue Version des Formats bereitzustellen. Können bestehende Consumer mehrere Versionen nicht sicher parallel verarbeiten, ist zusätzlich ein neuer Kanal vorzusehen, zum Beispiel eine eigene Queue, ein eigenes Topic oder ein eigener Event-Typ. Dieser Kanal ist dann für die neue Version der Consumer vorgesehen. Dies bedeutet: Wenn bestehende Consumer eine geänderte Nachricht oder ein Event nicht mehr korrekt verarbeiten können, dann darf das bestehende Format nicht einfach verändert werden, vielmehr muss ein neues, getrenntes Nachrichtenformat definiert werden.

2.1.4. Neues Backend

Ein neues Backend ist nicht die Standardmaßnahme bei Versionierung. Es ist dann sinnvoll, wenn:

  • sich die fachliche Verarbeitung zwischen den Versionen nicht mehr mit vertretbarem Aufwand gemeinsam abbilden lässt,

  • mehrere inkompatible Versionen langfristig parallel betrieben werden müssen, oder

  • nicht-funktionale Anforderungen eine getrennte Bereitstellung erfordern.

In allen anderen Fällen sollen mehrere Versionen durch dasselbe Backend unterstützt werden.

2.2. Entscheidungsraster

Kommunikationsmuster Änderungstyp Empfohlene Maßnahme

Synchroner Service

abwärtskompatibel

Bestehende Schnittstelle erweitern

Synchroner Service

nicht abwärtskompatibel

Neue Service-Version bereitstellen (in der Regel über eine zusätzliche Service-Schnittstelle realisiert)

Nachricht/ Event

abwärtskompatibel

Bestehendes Nachrichtenformat/ Event erweitern

Nachricht/ Event

nicht abwärtskompatibel

Neue Version des Event-/ Nachrichtenformats bereitstellen; bei fehlender Parallelverarbeitbarkeit zusätzlicher neuer Kanal

3. Einordnung in die Referenzarchitektur

Die in Entscheidungsmodell zur Versionierung beschriebenen Entscheidungen werden für synchrone Services in der Referenzarchitektur der IsyFact in der Regel innerhalb einer Serviceschicht umgesetzt.

Erfordert eine nicht abwärtskompatible Änderung eine neue Service-Version, so wird diese im Regelfall als eigene Service-Schnittstelle in der Serviceschicht des Backends bereitgestellt. Mehrere Service-Versionen können dabei denselben Anwendungskern verwenden, sofern sich die fachliche Verarbeitung mit vertretbarem Aufwand gemeinsam abbilden lässt.

Die für die Versionierung notwendigen Transformationen sind Teil der jeweiligen Service-Schnittstelle. Hierzu gehören insbesondere das Ergänzen von Standardwerten, die Abbildung unterschiedlicher Datenstrukturen sowie die Behandlung versionsspezifischer Unterschiede. Fachlogik des Anwendungskerns soll dabei nicht dupliziert werden.

Eine Trennung innerhalb des Anwendungskerns oder die Bereitstellung eines eigenen Backends kann sinnvoll sein, wenn sich die fachliche Semantik zwischen den Versionen wesentlich unterscheidet, mehrere inkompatible Verarbeitungslogiken langfristig parallel unterstützt werden müssen oder die gemeinsame Realisierung nicht verhältnismäßig ist. Die Entscheidung hierfür ist im Systementwurf zu dokumentieren.

Daraus folgen für synchrone Services die folgenden Grundsätze:

  • Eine neue Service-Version wird in der Regel als eigene Service-Schnittstelle in der Serviceschicht umgesetzt.

  • Verschiedene Service-Versionen verwenden denselben Anwendungskern.

  • Versionsspezifische Transformationen erfolgen grundsätzlich in der Service-Schnittstelle des Backends.

  • Ein Service-Gateway übernimmt keine Fachlogik zur Behandlung unterschiedlicher Versionen.

Externe Services werden durch Service-Gateways bereitgestellt.

Ein Service-Gateway ist ein technischer Zugriffspunkt zwischen externen Systemen und internen Services. Fachlogik oder komplexe Orchestrierungen finden im Gateway nicht statt. Die Behandlung von Versionsunterschieden erfolgt daher grundsätzlich im Backend und nicht im Service-Gateway.

Es ist möglich, pro Service-Version ein eigenes Service-Gateway zu erstellen, sofern dies aus technischen oder betrieblichen Gründen erforderlich ist.

Für den Standardfall einer nicht abwärtskompatiblen Änderung, bei gleichbleibender fachlicher Logik, die kein neues Backend erforderlich macht, ergibt sich folgende Architektur:

versionierung.dn
Abbildung 1. Standardfall für die Architektur versionierter Services

4. Versionierung synchroner Services

Dieses Kapitel konkretisiert das im Entscheidungsmodell zur Versionierung definierte Modell für synchrone Services.

Bei synchronen Services wird die veröffentlichte Schnittstelle eines Services versioniert. Die fachliche Identität des Services bleibt dabei erhalten. Eine neue Version ist nur dann eine Version desselben Services, wenn weiterhin dasselbe fachliche Konzept bereitgestellt wird. Eine neue Schnittstellenversion erfordert kein neues Backend. Im Regelfall werden mehrere Schnittstellenversionen durch dasselbe Backend bereitgestellt. Ein neues Backend ist nur dann vorzusehen, wenn fachliche Verantwortung, Datenhoheit oder Betriebsanforderungen ein eigenes IT-System erfordern.

4.1. Zuordnung zum Entscheidungsmodell

Die Auswahl eines Musters erfolgt auf Basis des Entscheidungsmodell zur Versionierung:

entscheidungsbaum version sync services.dn
Abbildung 2. Entscheidungsbaum für synchrone Service Versionierung

Diese Zuordnung ist im Systementwurf zu dokumentieren und zu begründen.

4.2. Zuordnung der Entscheidungsdimensionen zu Mustern

Muster Konsequenz Kurzbeschreibung

Kompatible Weiterentwicklung

Kompatible Weiterentwicklung bestehender Schnittstelle im bestehenden Backend

Die bestehende Service-Schnittstelle wird kompatibel erweitert. Keine Parallelversion notwendig.

Fachliche Schnittstellenerweiterung

Neue fachliche Schnittstelle im bestehenden Backend

Es entsteht eine neue fachliche Verantwortung, der bestehende Anwendungskern kann aber weiterverwendet werden. Keine Versionierung einer bestehenden Schnittstelle, sondern Ergänzung einer Neuen.

Fachliche Entkopplung in eigenem Backend

Neues Backend mit fachlicher Schnittstelle

Es entsteht eine neue fachliche Verantwortung, die nicht mehr durch den bestehenden Anwendungskern abgebildet werden kann.

Schnittstellenversionierung mit gemeinsamem Anwendungskern

Neue versionierte Service-Schnittstelle im bestehenden Backend (DTOs und Mapper je Version)

Die fachliche Verantwortung bleibt gleich, der bestehende Anwendungskern ist weiter nutzbar, aber die technische Schnittstelle ändert sich jedoch inkompatibel.

Implementierungsversionierung im gemeinsamen Backend

Versionierte Service-Implementierung im bestehenden Backend mit getrennten Anwendungskomponenten

Die fachliche Verantwortung bleibt gleich, aber eine sich unterscheidende fachliche Kernlogik muss parallel betrieben werden.

Vollständig entkoppelte Service-Version

Neue versionierte Service-Schnittstelle in neuem Backend

Die fachliche Verantwortung bleibt gleich, aber Datenhoheit, Lifecycle oder Betrieb erfordern vollständige Entkopplung.

4.3. Kompatible Weiterentwicklung eines Services

Ein Beispiel für die hier behandelten Änderungen ist das Hinzufügen eines optionalen Attributs zu einer bestehenden Operation.

Abwärtskompatible Änderungen werden innerhalb derselben Service-Version umgesetzt. Die bestehende Service-Schnittstelle wird erweitert, ohne bestehende Nutzende zu beeinträchtigen. Dies ist möglich, wenn die Änderungen vollständig abwärtskompatibel sind, die Clients unbekannte Felder ignorieren können und keine Änderung der fachlichen Semantik bei bestehenden Feldern vorgenommen wurde.

Hierfür ist die bestehende Service-Schnittstelle zu erweitern. Für neue Felder sind in der Serviceschicht Defaultwerte einzutragen.

Ein Backend stellt beispielsweise einen Service bereit, mit dem Personendaten gemeldet werden können. Parameter dieser Meldung sind Vor- und Nachname sowie das Geburtsdatum. Dazu gibt es einen Meldung-Service in der Version 1.0. Dieser wird in der Serviceschicht des Backends implementiert. Ab einem Stichtag soll zusätzlich noch der Geburtsort gemeldet werden. Im bisherigen Datenbestand wird dieses neue Attribut auf den Wert "unbekannt" gesetzt. Der bestehende Service wird um dieses Attribut erweitert. Da es sich um eine abwärtskompatible Änderung handelt, bleibt es bei derselben Service-Version. Anwendungskern und Persistenzschicht müssen ebenfalls erweitert werden.

Die Serviceschicht stellt sicher, dass Aufrufe ohne Angabe des Geburtsorts weiterhin korrekt verarbeitet werden können, indem für das fehlende Attribut der Wert "unbekannt" ergänzt wird.

Wird der Service durch ein Service-Gateway nach außen verfügbar gemacht, bleibt das Gateway auf dieselbe Service-Schnittstelle geroutet. Innerhalb des Gateways findet keine fachliche Abbildung statt. Die Behandlung fehlender optionaler Werte erfolgt im Backend.

Mit diesem Vorgehen ist kein Parallelbetrieb erforderlich. Dadurch ergibt sich minimaler Betriebsaufwand und keine zusätzliche Komplexität. Die Änderungsfreiheit ist jedoch eingeschränkt.

4.3.1. Auswirkungen

  • Deployment: Redeployment (Update eines bestehenden Deployments in neuer Version)

  • Betrieb: keine zusätzlichen Instanzen

  • Komplexität: gering

4.4. Inkompatible Veränderung eines Services

Ist eine Änderung an einem Service nicht abwärtskompatibel, ist gemäß dem Entscheidungsmodell zur Versionierung eine neue Service-Version bereitzustellen. Eine neue Version ist dann erforderlich, wenn eine Änderung nicht abwärtskompatibel ist und bestehende Nutzende nicht ohne Anpassung weiterarbeiten können.

Kann die neue Version mit vertretbarem Aufwand auf dieselbe fachliche Verarbeitung abgebildet werden, so können mehrere Service-Versionen denselben Anwendungskern nutzen. In diesem Fall enthält die Serviceschicht für jede Version eine eigene Service-Schnittstelle mit der jeweils erforderlichen Transformationslogik.

Wird in so einem Fall ein neuer Service eingeführt, während der alte Service noch verfügbar bleiben muss, müssen die inkompatiblen Verarbeitungslogiken im Anwendungskern parallel unterstützt werden. Bei starken fachlichen oder technischen Unterschieden kann es sinnvoll sein, die neue Version in einem eigenen Backend bereitzustellen. Auch in diesem Fall enthält das Service-Gateway keine Fachlogik.

Führen Änderungen zu einer wesentlichen fachlichen Neuausrichtung der Schnittstelle, soll keine weitere Version derselben Schnittstelle entstehen. In diesem Fall ist zu prüfen, ob es sich fachlich noch um dasselbe Konzept handelt. Wenn Versionen nicht mehr durch einfache Transformation verbunden werden können, handelt es sich nicht mehr um Versionen desselben fachlichen Konzepts, sondern die Einführung eines neuen fachlichen Konzepts. In diesem Fall ist zu prüfen, ob eine eigenständige neue Schnittstelle oder ein eigenes Backend die fachlich und technisch sauberere Lösung darstellt.

4.4.1. Neue versionierte Service-Schnittstelle im gemeinsamen Backend

Ein Beispiel für die hier behandelte Änderungen ist die Aufspaltung eines Feldes in mehrere Felder bei gleichbleibender fachlicher Logik. Das Backend stellt mehrere Versionen der veröffentlichten Service-Schnittstelle bereit. Hierfür ist eine zusätzliche Service-Schnittstelle in der Serviceschicht vorzusehen. Versionsspezifische Unterschiede sind in der Serviceschicht zu behandeln. Dazu gehören versionierte Schnittstellenendpunkte, versionierte Request- und Response-DTOs, Mapper zwischen Schnittstellenmodell und internem Modell und Defaultwerte sowie technische Adaptierungen. Mehrere Versionen werden parallel betrieben und nutzen denselben Anwendungskern.

Dies ist das angemessene Vorgehen für Änderungen, die nicht abwärtskompatibel sind, aber die fachliche Logik gleich bleibt. Ein Mapping zwischen Versionen ist möglich.

Mit diesem Vorgehen wird eine kontrollierte Migration möglich. Eine Duplikation der Fachlogik ist nicht notwendig und der Infrastrukturaufwand bleibt gering. Die Komplexität in der Serviceschicht ist aber höher da eine Mapping-Logik notwendig ist. Zu beachten ist, dass bei vielen Versionen die Wartungskosten steigen.

Auswirkungen:

  • Deployment: Redeployment mit mehreren Schnittstellenversionen (Update eines bestehenden Deployments in neuer Version)

  • Betrieb: gemeinsame Skalierung

  • Komplexität: mittel

4.4.2. Versionierte Service-Implementierung im bestehenden Backend

Ein Beispiel für die hier behandelten Anpassungen ist eine unterschiedliche fachliche Validierungslogik zwischen Versionen. Die Änderung ist nicht abwärtskompatibel oder die fachliche Logik beginnt zu divergieren, damit ist die vollständige Wiederverwendung nicht mehr sinnvoll. Das Backend bleibt dieselbe Systemeinheit. Innerhalb des Backends werden jedoch getrennte Implementierungspfade für die Versionen geführt. Ein gemeinsamer Anwendungskern ist nur zulässig, solange die Versionen dieselbe fachliche Semantik abbildet. Versionsspezifische Fachlogik darf nicht in der Service-Schnittstelle implementiert werden. Sie ist in getrennten Anwendungskomponenten oder getrennten Implementierungspfaden des Anwendungskerns zu kapseln. Die Serviceschicht vermittelt nur zur jeweils passenden Implementierung. Eine angemessene Wartbarkeit bei wachsender Divergenz wird durch die Wiederverwendung gemeinsamer Infrastrukturkomponenten erreicht. Die erhöhte Komplexität im Backend und eine teilweise Duplikation von Logik, was sich in steigendem Testaufwand niederschlägt, ist bei diesem Vorgehen als negativ zu betrachten. Bei zunehmender fachlicher Divergenz ist die Minimierung horizontaler Komplexität wichtiger als die Vermeidung technischer Redundanz.

Auswirkungen:

  • Deployment: ein Deployment mit differenzierter Logik

  • Betrieb: gemeinsame Laufzeit, Monitoring jeder Version notwendig

  • Komplexität: mittel bis hoch

Das Muster "Versionierte Service-Implementierung im bestehenden Backend" darf nicht zu versionsabhängigen Verzweigungen im Anwendungskern führen.

Zulässig sind getrennte Implementierungsklassen, getrennte Anwendungskomponenten, explizite Auswahl der Implementierung durch die Service-Schnittstelle und eine gemeinsame Nutzung technischer Infrastruktur.

Nicht zulässig sind weder verstreute if/else-Logik nach Version, fachliche Regeln in DTO-Mappern, langfristige Kopplung divergierender Fachmodelle, noch implizite Unterscheidung über optionale Felder.

4.4.3. Neue Version als eigenes Backend

Beispiel: Technologiewechsel oder grundlegende fachliche Neuausrichtung eines Services.

Eine neue Service-Version wird als eigenständiges Backend realisiert. Dies ist nur bei hoher fachlicher oder technischer Divergenz sinnvoll, insbesondere wenn ein langfristiger Parallelbetrieb erforderlich ist. Jedes Backend besitzt eine eigene Serviceschicht, einen eigenen Anwendungskern, ein eigenes Deployment und eigene Betriebsanforderungen. Über die Datenhaltung ist explizit zu entscheiden. Die gemeinsame direkte Nutzung derselben Datenbank durch mehrere Backend-Versionen ist grundsätzlich zu vermeiden. Sie koppelt die Backends über das Datenmodell, verhindert unabhängige Weiterentwicklung und macht Änderungen riskant.

Zulässig sind nur bewusst begründete Übergangslösungen, etwa während einer Migration. Diese sind mit Bezug auf Umfang und Dauer im Systementwurf zu dokumentieren.

Als Vorteil ergibt sich eine maximale Entkopplung und eine unabhängige Weiterentwicklung mit klaren Systemgrenzen.

Ein neues Backend ist keine Standardmaßnahme für Versionierung. Es ist eine bewusste Architekturentscheidung und im Systementwurf zu begründen.

Auswirkungen:

  • Deployment: separate Deployments / Container

  • Betrieb: getrennte Skalierung und Überwachung

  • Komplexität: hoch

4.5. Getrennte Backends

Die Aufspaltung eines Backends in zwei eigenständige Systeme bietet den höchsten Grad der Entkopplung, ist als Maßnahme bei Breaking Changes jedoch mit der größten Tragweite verbunden und sollte daher möglichst vermieden werden. Ist dies dennoch aus den im Entscheidungsmodell zur Versionierung beschriebenen Gründen notwendig, sollten folgende Konsequenzen beachtet werden:

  1. Mehraufwand durch zusätzliche Codebasis,

  2. gestiegener Ressourcenverbrauch bei Parallelbetrieb der neuen Version,

  3. Mehraufwand im Betrieb und Monitoring,

  4. gestiegene Komplexität in der Infrastruktur,

  5. höheres Risiko von Dateninkonsistenzen bei Nutzung einer gemeinsamen Datenbank.

Wenn getrennte Backends nur lesen, ist die Nutzung einer gemeinsamen Datenbank hinsichtlich Datenkonsistenz in der Regel unkritisch. Bei schreibendem Zugriff sollte über eine Trennung der Datenbanken bzw. Datenbank-Schemas nachgedacht werden.

Es gibt zwei Arten von getrennten Backends bei der Versionierung:

  • Backend-Versionen

  • und Backend-Typen

Getrennte Backend-Versionen entstehen bei technisch inkompatiblen Änderungen, die eine weitere Version von Schnittstelle und Anwendungskern erfordern, die fachliche Verantwortung aber erhalten bleibt. Dabei entstehen zwei voneinander unabhängige IT-Systeme mit der jeweiligen Backend-Version, wie in Abbildung Getrennte Backend-Versionen nach inkompatibler Änderung dargestellt.

getrennte backend versionen.dn
Abbildung 3. Getrennte Backend-Versionen nach inkompatibler Änderung

Ist hier keine Trennung in der Persistenzschicht möglich, kann ein weiterer Service Abhilfe schaffen, der durch Transformationslogik die Datenkonsistenz insbesondere bei konkurrierenden Schreibzugriffen oder divergierenden Datenmodellen sicherstellt. Ein weiterer Service bringt allerdings mehr Komplexität in die Anwendungslandschaft.

Ein neuer Backend-Typ neben dem bestehenden entsteht, wenn sich die fachliche Verantwortung ändert. Abbildung Getrennte Backends nach neuem Backend-Typ veranschaulicht die Situation erneut. Darin wird aus dem fachlichen Konzept Meldung das Konzept Abruf.

getrennte backend typen.dn
Abbildung 4. Getrennte Backends nach neuem Backend-Typ
Bei der Änderung des fachlichen Konzepts ändert sich auch die fachliche Sprache, welche im Sinne von Domain Driven Design über Teams hinweg genutzt wird. Daher wird spätestens an dieser Stelle eine Auftrennung in der Persistenzschicht empfohlen, um Dateninkonsistenzen durch Missverständnisse zu vermeiden.

Wegen der Zusatzkosten, die beim Parallelbetrieb von getrennten Backends entstehen, sollten mehr als zwei Systemversionen bzw. Systemtypen vermieden werden. Nach Möglichkeit wird eine zeitnahe Umstellung und Ablösung des bestehenden Backends empfohlen.

4.5.1. Migration von Backends

Eine Migration zu einer neuen Backend-Version bzw. einem neuen Backend-Typ kann vollständig in einem Schritt erfolgen (Big-Bang), vorausgesetzt eine Betriebsunterbrechung ist tolerabel.

Vor der Umstellung ist ein Migrationsplan zu erstellen und mit allen Betroffenen abzustimmen. Besonders wichtig ist es, im Falle eines Scheiterns die Daten vollständig wiederherzustellen und den vorherigen Betriebszustand wiederherstellen zu können (Rollback).

Die Umstellung auf das neue Backend, die neuen Service-Clients und gegebenenfalls die neue Datenbank erfolgt innerhalb eines Wartungsfensters, das vorzugsweise in einem Zeitraum mit geringer Last liegt.

Wenn eine Betriebsunterbrechung nicht möglich ist, werden die getrennten Backends parallel betrieben. Bei einer Trennung in der Persistenzschicht muss zu Beginn die bestehende Datenbank repliziert werden und Änderungen der alten Datenbank (z.B. durch Event-Streaming) auf die neue Datenbank zurückgespiegelt werden.

Wenn alle alten Service-Clients umgestellt sind, kann das alte Backend eingestellt werden. Damit finden keine Schreiboperationen mehr auf die alte Datenbank statt, sondern nur noch auf die neue. Die alte Datenbank kann während einer Stabilitätsphase weiterhin im Lesebetrieb verbleiben, um gesetzliche Auskunftspflichten zu erfüllen, oder anschließend archiviert werden.

5. Versionierung externer Schnittstellen

Externe Service-Schnittstellen stellen Services der eigenen Anwendungslandschaft IT-Systemen außerhalb der eigenen Organisation bereit. Daher liegt der Fokus im Vergleich zu internen Schnittstellen stärker auf Stabilität, Sicherheit und klaren API-Verträgen.

Bei inkompatiblen Änderungen können Schnittstellen auf zwei Arten versioniert werden:

  • innerhalb des bestehenden Service-Gateways

  • in eigenen Versionen des Service-Gateways

Bei der Versionierung innerhalb desselben Gateways wird neben dem bestehenden Provider- und Adapter-Paar eine neue Version erstellt, welche die neuen Anfragen verarbeitet.

versionierung innerhalb gateway.dn
Abbildung 5. Schnittstellenversionierung innerhalb des bestehenden Gateways

Bei der Versionierung in eigenen Gateway-Versionen wird die neue Version des Provider- und Adapter-Paares in eine eigene Version des Service-Gateways implementiert. Dieser Ansatz bietet eine höhere Entkopplung und wird wegen des erwähnten Fokus auf Stabilität bevorzugt.

versionierte gateways.dn
Abbildung 6. Schnittstellenversionierung in eigene Versionen von Gateways

Beide beschriebenen Varianten sind eine Anwendung der in der Referenzarchitektur definierten Systemarchitektur von Gateways.

6. Versionierung von Nachrichten

Dieses Kapitel legt allgemeine Richtlinien für die Weiterentwicklung von Nachrichtenstrukturen in nachrichtenbasierten Systemen fest. Grundsätzlich gilt: Eine Nachrichtenstruktur darf nur so geändert werden, dass Producer und Consumer weiterhin unabhängig voneinander betrieben und migriert werden können.

Vor der Umsetzung ist jede Änderung als kompatibel oder inkompatibel einzustufen. Eine Änderung gilt für bestehende Kanäle nur dann als kompatibel, wenn sie vollständig kompatibel ist, also Abwärts- und Vorwärtskompatibilität gegeben sind oder die betroffenen Producer und Consumer kontrolliert in einer abgestimmten Reihenfolge migriert werden.

Vollständig kompatible Änderungen können auf demselben Topic, derselben Queue oder demselben Event-Typ umgesetzt werden.

Inkompatible Änderungen, also Breaking Changes, dürfen nicht durch eine Änderung des bestehenden Nachrichtenformats auf demselben Kanal eingeführt werden. Standardmäßig ist hierfür ein neuer Event-Typ, ein neuer Routing-Key, ein neues Topic oder eine neue Queue vorzusehen. Die kontrollierte Weiterentwicklung einer Nachrichtenstruktur wird als Schema Evolution bezeichnet.

Aufgrund der Eigenschaften nachrichtenbasierter Systeme sind diese Änderungen mit Risiken für die korrekte Funktionsweise des angepassten Systems verbunden, weil Nachrichten entkoppelt, asynchron und häufig über längere Zeit persistiert werden

Fehlerhafte Schema Evolution kann zu Deserialisierungsfehlern, blockierten Queues, fehlerhaften Replays oder stillen fachlichen Fehlinterpretationen führen. Das relevanteste Risiko ist nicht ein offensichtliches Systemversagen, sondern der stille semantische Fehler: Der Konsument verarbeitet eine formal gültige Nachricht, sie wird aber fachlich falsch interpretiert. Die fachliche Kompatibilität ist daher zusätzlich zur technischen Schema-Kompatibilität zu prüfen.

Neue Felder auf bestehenden Kanälen müssen standardmäßig optional sein oder einen fachlich sicheren Default haben. Ein neues Pflichtfeld ist nur erlaubt, wenn nachweislich alle Produzenten und alle relevanten historischen Nachrichten das Feld liefern oder wenn ein neuer Kanal/Event-Typ eingeführt wird.

Ein bestehender Kanal darf nur weiterverwendet werden, wenn die Änderung für alle relevanten Consumer kompatibel ist, historische Nachrichten weiterhin verarbeitet werden können und keine fachliche Bedeutungsänderung bestehender Felder oder Events vorliegt. Ein neuer Event-Typ, Routing-Key, Topic oder eine neue Queue ist erforderlich, wenn die Änderung ein Breaking Change ist oder wenn alte und neue Consumer nicht sicher auf demselben Kanal parallel betrieben werden können.

Die folgenden Änderungen sind typische Breaking Changes:

Tabelle 1. Typische Breaking Changes bei Nachrichten und Events
Änderung Erläuterung

Pflichtfeld hinzufügen

Alte Nachrichten und alte Produzenten enthalten es nicht.

Pflichtfeld entfernen

Bestehende Consumer können neue Nachrichten nicht mehr korrekt verarbeiten, wenn sie das Feld fachlich oder technisch voraussetzen.

Feld umbenennen

Entspricht technisch dem Entfernen des alten Feldes und dem Hinzufügen eines neuen Feldes. Bestehende Consumer finden das erwartete Feld nicht mehr.

Feldtyp ändern

Konsumenten erwarten den alten Typ.

Semantik eines Feldes ändern

Technisch gleich, fachlich anders.

Einheit ändern

Beispiel: amount von Euro zu Cent.

Enum-Wert entfernen

Konsumenten können alte Werte nicht mehr interpretieren.

Enum-Wert hinzufügen

Bestehende Consumer können den neuen Wert nur dann sicher verarbeiten, wenn sie unbekannte Enum-Werte explizit behandeln. Andernfalls ist die Änderung nicht kompatibel.

Bedeutung eines Events ändern

Konsumenten reagieren fachlich falsch.

Kardinalität ändern

Beispiel: Id wird zu Ids.

Struktur verschachteln oder abflachen

Feldpfade ändern sich.

Reihenfolge/Key-Semantik ändern

Besonders kritisch bei Kafka-Partitionierung.

Eine Schema Registry ist sinnvoll, sobald mehr als ein Team oder mehr als ein Consumer beteiligt ist. Für Events wird dies in Schema Management konkretisiert.

Zusammenfassend ist für die nachrichtenbasierte Systeme zu beachten:

  • Nachrichtenverträge sind versionierte öffentliche Schnittstellen.

  • Bestehende Felder dürfen nicht umbenannt, entfernt, typverändert oder semantisch verändert werden, solange aktive oder replayfähige Konsumenten existieren.

  • Für den Betrieb der alten und neuen Version ist eine für Migrationsstrategie festzulegen. Die Abschaltung der alten Version erfolgt gemäß Lifecycle und Deprecation von Versionen.

  • Änderungen an Bedeutung, Einheit, Kardinalität, Identität, Ordering oder Idempotenz gelten als potenziell breaking, auch wenn das Schema formal kompatibel bleibt.

7. Versionierung von Events

Dieses Kapitel konkretisiert den allgemeinen Umgang mit Änderungen an Nachrichten aus Versionierung von Nachrichten für Events in eventbasierten Systemen wie z.B. Kafka. Events stellen fachliche Ereignisse dar und unterliegen daher neben der technischen Schema-Kompatibilität auch besonderen Anforderungen an Semantik, Datenkonsistenz, Replay und Idempotenz. Nach der Lektüre sollte der Leser in der Lage sein, diese richtig einzuordnen und mithilfe der definierten Entscheidungsmatrix die geeignete Vorgehensweise bei der Versionierung zu finden.

7.1. Fachliche Änderungen

In der asynchronen Kommunikation werden Events genutzt, um über geschäftsrelevante Ereignisse innerhalb der Anwendungslandschaft zu informieren. Aus der Sicht von konsumierenden Systemen sind Events wichtige historische Ereignisse (Fakten), die für die Weiterverarbeitung benötigten Daten enthalten.

Ihre Semantik wird beim Domain Driven Design bereits zu Beginn beschrieben (z.B. im Rahmen von Event Storming Sitzungen). Es ist wichtig diese auch in der Dokumentation für alle Beteiligten Teams verbindlich festzuhalten (Design By Contract).

Folgende Tabelle zeigt vereinfacht im Stil einer Karteikarte, wie die Definition der Semantik eines Events für eine aufgegebene Bestellung aussehen könnte.

Tabelle 2. Definition der Semantik für das Event BestellungAufgegeben (Version 1)

Name

BestellungAufgegeben

Version

1

Domäne

Bestellung

Eigentümer

Team A

Beschreibung

Der Kunde hat eine Bestellung verbindlich aufgegeben. Die Bestellung wurde erfolgreich im System persistiert.

Auslöser

Das Event wird erzeugt, nachdem der Kunde den Kauf bestätigt hat und die Bestellung erfolgreich gespeichert wurde.

Felder

artikelId

Eindeutige Kennung des Artikels, um den bestellten Artikel zu identifizieren. Typ String.

anzahl

Anzahl des bestellten Artikels. Typ Integer.

preis

Einzelpreis des Artikels. Typ Float.

Fachliche Änderungen wirken sich auf die Semantik aus und führen immer zu Breaking Changes. Im Gegensatz zu technisch inkompatiblen Änderungen können sie nicht automatisch erkannt werden.

Folgende Tabelle zeigt beispielhaft die semantische Änderung des Feldes preis. Zuvor war der Preis für den einzelnen Artikel gemeint, nun soll das Feld den Gesamtbetrag für alle Artikel enthalten. Da Feldname und Datentyp dieselben sind, wissen Consumer von der Änderung nichts und berechnen weiterhin den Gesamtpreis durch Multiplikation mit der Anzahl.

Tabelle 3. Definition der Semantik für das Event BestellungAufgegeben mit Änderung (Version 2)

Name

BestellungAufgegeben

Version

2

…​

Felder

…​

preis

Gesamtbetrag der bestellten Artikel. Typ Float.

7.2. Technische Änderungen

Änderungen an der Struktur von Nachrichten bei bleibender Semantik werden als technische Änderungen bezeichnet. Technische Änderungen sind in kompatible und inkompatible technische Änderungen untergliedert. Strukturelle Breaking Changes können bei der Nachrichtenvalidierung automatisch erkannt werden.

7.3. Nachrichtenvalidierung

Systeme tauschen Events in einem wohldefinierten Format untereinander aus. Wie die Struktur einer Nachricht konkret aufgebaut ist (Syntax), wird in einem Schema festgehalten. Das vereinbarte Schema wird von Producern verwendet, um vor dem Versenden die Nachricht zu validieren. Der Consumer verwendet es für die Validierung der empfangenen Nachricht (Design by Contract).

Die Verwendung von Schemata stellt die korrekte Kommunikation zwischen den Teilnehmern und damit die Funktion insbesondere von Consumern sicher.

Folgendes Listing in der Definitionssprache Avro zeigt, wie das Event BestellungAufgegeben aufgebaut ist. Darin ist z.B. das Feld artikelId als String definiert und ist verpflichtend in den daraus abgeleiteten Events.

Listing 1. Schema für den Event-Typ BestellungAufgegeben (Version 1)
{
  "type": "record",
  "name": "BestellungAufgegeben",
  "fields": [
    { "name": "artikelId", "type": "string" },
    { "name": "anzahl", "type": "int" }
  ]
}
Avro wird wegen der leichten Verständlichkeit auch in den Folgebeispielen genutzt. Die Version wird in Klammern als fortlaufende Ganzzahl zwecks Verweis angegeben.

7.3.1. Abwärts- und Vorwärtskompatibilität

In Literatur und Dokumentationen kommt es gelegentlich zu Verwechslungen zwischen Abwärts- und Vorwärtskompatibilität. Daher im Folgenden eine kurze Definition der beiden zur Abgrenzung.

Ein Schema ist abwärtskompatibel, wenn bestehende Consumer unter Verwendung der alten Schema-Version in der Lage sind, Nachrichten korrekt zu verarbeiten, die mit der neuen Schema-Version erzeugt wurden. Vereinfacht formuliert: alte Consumer können neue Nachrichten lesen.

Ein Schema ist hingegen vorwärtskompatibel, wenn aktualisierte Consumer unter Verwendung der neuen Schema-Version in der Lage sind, Nachrichten korrekt zu verarbeiten, die mit der alten Schema-Version erzeugt wurden. Vereinfacht formuliert: neue Consumer können alte Nachrichten lesen.

7.4. Datenkonsistenz

In Event-basierten Systemen wie Kafka werden Nachrichten in Logdateien in der Regel für einen längeren Zeitraum persistiert (Event-Log). Daher müssen im Vergleich zu Queue-basierten Systemen Consumer in der Lage sein, auch alte Nachrichten zu lesen, wenn sie wieder eingespielt werden (Replay), etwa beim Eintritt einer neuen Consumer-Group oder Zurücksetzen des Eventstroms (Offset Reset).

Eine standardisierte Schema-Verwaltung spielt für den Erhalt der Datenkonsistenz eine wichtige Rolle.

7.5. Replay und Idempotenz

Neue Versionen von Events müssen so aufgebaut sein, dass weiterhin ein Verarbeiten von alten Nachrichten (Replay) möglich ist. Ein Event muss dazu in sich abgeschlossen und vollständig bleiben und es darf nicht von der externen Systemumgebung abhängen. So wird sichergestellt, dass durch Replay keine fachlichen Inkonsistenzen entstehen.

Bei einer erneuten Nachrichtenzustellung muss die Verarbeitungslogik der neuen Consumer weiterhin Duplikate anhand einer eindeutigen Event-ID erkennen und ignorieren, um Mehrfachverarbeitungen zu vermeiden.

7.6. Schema Management

Schemata ändern sich fortlaufend mit neuen Anforderungen. Um Wildwuchs von Versionen zu vermeiden, werden sie an einer zentralen Stelle z.B. Wiki oder git-Repository von Entwicklerteams gemeinsam verwaltet.

In der Praxis hat sich der Einsatz von Schema Registries wie Apicurio Registry oder Confluent Schema Registry bewährt. Der große Vorteil den Schema Registries mit sich bringen ist die Erkennung von Schemabrüchen während der Schema-Validierung. Damit sind Breaking Changes durch Anpassungen am Schema praktisch ausgeschlossen, weil nur kompatible Änderungen zugelassen werden.

Die Vorteile von Schema Registries erkauft man sich u.a. durch die gestiegene Komplexität und Aufwände für den Betrieb eines zusätzlichen Systems in der Anwendungslandschaft.

Ob Entwicklerteams sich für ein einfaches Schema Management mit Wiki oder einer Schema Registry entscheidet, ist Abwägungssache. Wichtig ist nur, dafür eine zentrale Stelle zu verwenden, um Schemabrüche und Versionswildwuchs zu vermeiden.

7.7. Schema Evolution vs. Schemabruch

In den meisten Fällen handelt es sich bei den Änderungen an Event-Typen um abwärtskompatible Änderungen. Kommt beispielsweise das optionale Feld kommentar zur Version 1 des Event-Typs BestellungAufgegeben hinzu, entsteht eine neue Version aber kein neuer Event-Typ. Eine fortschreitende Versionierung von Event-Typen, die im Rahmen der Kompatibilität bleibt, wird als Schema Evolution bezeichnet.

Im Beispiel verwenden alte Consumer das Schema in der Version 1 beim Lesen von Nachrichten. Nachrichten, welche mit der Version 2 erzeugt wurden, enthalten zusätzlich das Feld kommentar. Da der alte Consumer dieses nicht kennt, wird es einfach ignoriert und die Verarbeitung geht normal weiter.

In Nachrichten, die mit der alten Schema-Version erzeugt wurden, fehlt das Feld kommentar. Das ist aber für neue Consumer kein Problem, da es optional ist und einfach mit dem Wert null belegt wird.

Listing 2. Schema für den Event-Typ BestellungAufgegeben mit optionalem Kommentarfeld (Version 2)
{
  "type": "record",
  "name": "BestellungAufgegeben",
  "fields": [
    { "name": "artikelId", "type": "string" },
    { "name": "anzahl", "type": "int" },
    { "name": "kommentar", "type": ["null" ,"string"], "default": null }
  ]
}

In Fällen, bei denen Änderungen zum Bruch der Kompatibilität führen (Breaking Change) reicht eine neue Schema-Version nicht mehr aus, d. h. es liegt ein Schemabruch vor. Als Folge muss ein neues Schema für einen neuen Event-Typ erstellt werden.

Im Beispiel-Event BestellungAufgegeben könnte ein neues Pflichtfeld preis dazukommen. Bei fortschreitender Versionierung könnten neue Consumer neue Nachrichten zwar lesen, aber bei den alten Nachrichten würde der Preis fehlen und der Consumer wüsste nicht, welcher Wert gemeint ist. Deswegen gibt es ein neues Schema für den Event-Typ BestellungMitPreisAufgegeben.

Im Falle von Breaking Changes wird dringend empfohlen, für das neue Schema auf generische Namen wie "BestellungAufgegebenV1" zu verzichten, um eine Vermischung von verschiedenen Versionskonzepten zu vermeiden, insbesondere wenn fortschreitende Versionen manuell z.B. über Header gesetzt werden.
Listing 3. Schema für den neuen Event-Typ BestellungMitPreisAufgegeben mit neuem Pflichtfeld (Version 1)
{
  "type": "record",
  "name": "BestellungMitPreisAufgegeben",
  "fields": [
    { "name": "artikelId", "type": "string" },
    { "name": "anzahl", "type": "int" },
    { "name": "preis", "type": "float" }
  ]
}

7.8. Entscheidungsmatrix neuer Event-Typ vs. Versionierung

Folgende Tabelle führt als Entscheidungshilfe auf, wann ein neuer Event-Typ in Abhängigkeit der Änderung nötig ist oder eine neue Version ausreicht.

Tabelle 4. Entscheidungsmatrix neuer Event-Typ nötig
Änderung Abwärtskompatibel Konsequenz

fachlich

egal

neuer Event-Typ

technisch

nein

neuer Event-Typ

technisch

ja

neue Version (Schema Evolution)

Fachliche Änderungen erfordern immer einen neuen Event-Typ. Breaking Changes erfordern immer einen neuen Event-Typ. Nur kompatible technische Änderungen können mit Schema Evolution versioniert werden.

8. Kanalstrategien bei Asynchroner Kommunikation

Bei inkompatiblen Änderungen an der Struktur von Nachrichten können bestehende oder neue Consumer alte Nachrichten nicht mehr korrekt verarbeiten. Um Stabilität und Datenkonsistenz zu gewährleisten, wird ein neues Topic für inkompatible Schemaänderungen erstellt. Im nächsten Schritt können Producer und Consumer auf das neue Topic migriert werden.

8.1. Versionierung von Queues/Topics

Für die Benennung von Topic-Versionen hat sich ein Muster etabliert, das auch bei REST-APIs für Breaking Changes verwendet wird.

Beispiel:

bestellung.erstellt.v1
bestellung.erstellt.v2
...

Eine Vergabe von Versionen nach dem Semantic Versioning sollte vermieden werden, da sie zu einer Vermischung von API- und Datenstromsemantik führt. Beispielsweise würde die Benennung eines Topics mit bestellung.erstellt.v1.2 eine kompatible Änderung suggerieren, ein neues Topic wird jedoch ausschließlich bei inkompatiblen Änderungen eingeführt. Versionen für kompatible Änderungen werden im Rahmen von Schema Evolution durch die Schema Registry verwaltet.

Wenn beispielsweise im Schema für Bestellungen der Datentyp des Feldes artikelId geändert wird, entsteht ein neues Schema mit einem Breaking Change. Mit der Verwendung des neuen Schemas befinden sich die alten Nachrichten weiterhin im alten Topic bestellung.erstellt.v1 und die mit dem neuen Schema erzeugten Nachrichten im Topic bestellung.erstellt.v2.

Listing 4. Schema BestellungIdLongErstellt mit geändertem Feldtyp (Version 1)
{
  "type": "record",
  "name": "BestellungIdLongErstellt",
  "fields": [
    { "name": "artikelId", "type": "long" },
    { "name": "anzahl", "type": "int" }
  ]
}

8.2. Migration von Topics/Queues

Die Strategie zur Migration von Topics hängt vom konkreten Anwendungsfall und den jeweiligen Anforderungen ab:

  1. Was passiert mit bestehenden Events - können diese gelöscht werden oder werden sie noch für spätere Analysen gebraucht, gegebenenfalls im neuen Format?

  2. Sind verlorene oder fehlerhaft verarbeitete Nachrichten während der Migration hinnehmbar?

  3. Darf der laufende Betrieb des Systems während der Wartung unterbrochen werden?

  4. Soll die Zustellreihenfolge der Nachrichten erhalten bleiben?

  5. Dürfen Nachrichten mehrfach verarbeitet werden (Duplikate)?

8.2.1. Einfache Migration (Big-Bang, mit Wartungsfenster)

Wenn eine kurzzeitige Unterbrechung des Betriebs akzeptabel ist (z.B. weil nachts wenige Anträge eingehen), kann die vollständige Migration während eines definierten Wartungsfensters in einem einzigen Schritt erfolgen, sofern sie mit den beteiligten Entwicklerteams und Abteilungen abgestimmt ist.

Während einer Wartungszeit werden bestehende Producer zuerst eingestellt, sodass keine Nachrichten mehr in das alte Topic geschrieben werden. Anschließend arbeiten die bestehenden Consumer alle noch im System befindlichen Nachrichten im alten Topic vollständig ab. Sobald alle Nachrichten im alten Topic vollständig verarbeitet sind, werden die neuen Producer und Consumer gestartet und arbeiten ausschließlich mit dem neuen Topic.

Der Ablauf gliedert sich in den Phasen Vorbereitung, Einfrieren der bestehenden Producer, Entleeren des bestehenden Topics, Umstellung auf das neue Topic und Dekommissionierung des alten Topics.

  1. Neues Topic antrag.v2 wird angelegt (Phase: Vorbereitung).

  2. Neue Producer und Consumer werden erstellt.

  3. Wartungsfenster wird aktiviert.

  4. Alte Producer werden eingestellt, damit der Datenbestand nicht weiter befüllt wird (Phase: Einfrieren).

  5. Bestehende Consumer arbeiten verbleibende Nachrichten in antrag.v1 vollständig ab (Phase: Entleeren).

  6. Neue Producer und Consumer werden gestartet und arbeiten ausschließlich mit dem neuen Topic antrag.v2 (Phase: Umstellung).

  7. Nach einer Stabilitätsphase geht das System wieder in Betrieb.

  8. Topic antrag.v1 kann gelöscht oder archiviert werden (Phase: Dekommissionierung).

Das Bild Big Bang Migration eines Topics veranschaulicht die wesentlichen Schritte der einfachen Migration mit Wartungsfenster.

big bang topic migration.dn
Abbildung 7. Big Bang Migration eines Topics

8.2.2. Migration nach dem Strangler Pattern (schrittweise, im laufenden Betrieb)

In kritischen Systemen wie Banksystemen sind Ausfälle und Datenverluste inakzeptabel. Hier ist eine Strategie erforderlich, die eine Migration während des laufenden Betriebs ermöglicht, ohne bestehende Funktionalität zu beeinträchtigen. Dies ist in der Praxis der empfohlene Weg.

Das Strangler Pattern dient dazu, ein bestehendes System schrittweise durch ein neues zu ersetzen. Dabei werden neue Komponenten parallel zum Bestandssystem aufgebaut, die inkrementell dessen Funktionalität übernehmen, bis das Bestandssystem abgelöst werden kann.

Dieses Prinzip lässt sich auf Topics/Queues mit inkompatiblen Änderungen übertragen.

Der Ablauf gliedert sich in den Phasen Vorbereitung, Parallelisierung des bestehenden Datenstroms, schrittweise Umstellung der Consumer, Umstellung der Producer und optional Dekommissionierung des alten Topics.

Zu Beginn wird ein neues Topic mit dem aktualisierten Schema bereitgestellt (Phase: Vorbereitung). Anschließend wird der alte Datenstrom vollständig in das neue Topic transformiert und repliziert (Phase: Parallelisierung). Dies erfolgt über eine Transformationskomponente, die Nachrichten aus dem alten Topic liest, in das neue Format überführt und in das neue Topic schreibt. Technisch kann dies beispielsweise durch ein Consumer-/Producer-Paar oder mittels Kafka Streams umgesetzt werden.

Zeitgleich werden neue Consumer schrittweise auf das neue Topic umgestellt (Phase: schrittweise Umstellung der Consumer). Voraussetzung hierfür ist, dass sowohl alte Daten als auch laufende Events im neuen Topic vollständig verfügbar sind.

Während der gesamten Übergangsphase existieren altes und neues Topic parallel, wobei der vollständige Datenbestand in beiden Systemen vorliegt (Replikation).

Sobald alle Consumer erfolgreich auf das neue Topic migriert sind, werden die Producer auf das neue Topic umgestellt (Phase: Umstellung der Producer). Die Umstellung der Producer muss atomar erfolgen, um Duplikate zu vermeiden.

Nach einer Stabilitätsphase kann das alte Topic außer Betrieb genommen, gelöscht oder archiviert werden (Phase: Dekommissionierung). Die Transformationskomponente wird anschließend ebenfalls entfernt.

Das Bild Migration eines Topics nach dem Strangler Pattern veranschaulicht nochmal die wichtigen Phasen der Migration nach dem Strangler Pattern.

strangler topic migration.dn
Abbildung 8. Migration eines Topics nach dem Strangler Pattern

Dieses Vorgehen ermöglicht eine Migration mit minimalem Risiko, ohne den laufenden Betrieb zu stören und mit kontrollierter Umstellung aller beteiligten Systeme. Es ist jedoch zu berücksichtigen, dass durch den Parallelbetrieb von neuem und altem Topic und der zusätzlichen Komponente Transformator Komplexität der Infrastruktur und Systemlast ansteigen. Änderungen sind daher mit den Teams für Betrieb und Monitoring abzustimmen.

9. Lifecycle und Deprecation von Versionen

Jede neue nicht abwärtskompatible Version muss eine Deprecation-Policy für die Vorgängerversion definieren.

Diese umfasst mindestens:

  • Datum der Einführung der neuen Version,

  • Datum der Deprecation der alten Version,

  • Mindestunterstützungszeitraum,

  • geplantes Abschaltdatum,

  • betroffene Clients bzw. Consumer,

  • Migrationsanleitung,

  • technische Metrik zur Nutzung der alten Version.

Die alte Version darf erst abgeschaltet werden, wenn alle betroffenen Service-Nutzenden bzw. Consumer migriert sind. Das Monitoring muss die erfolgreiche Migration bestätigen und Retention-, Replay- sowie weitere relevante Fristen müssen berücksichtigt sein.