Ich habe mich über Versionsstrategien für ReST-APIs informiert, und keine davon scheint sich mit der Verwaltung der zugrunde liegenden Codebasis zu befassen.
Nehmen wir an, wir nehmen eine Reihe von Änderungen an einer API vor, z. B. indem wir unsere Kundenressource so ändern, dass sie separate forename
und surname
Felder anstelle eines einzelnen name
Felds zurückgibt . (In diesem Beispiel verwende ich die URL-Versionierungslösung, da die Konzepte leicht zu verstehen sind. Die Frage gilt jedoch auch für die Aushandlung von Inhalten oder benutzerdefinierte HTTP-Header.)
Wir haben jetzt einen Endpunkt bei http://api.mycompany.com/v1/customers/{id}
und einen anderen inkompatiblen Endpunkt bei http://api.mycompany.com/v2/customers/{id}
. Wir veröffentlichen immer noch Bugfixes und Sicherheitsupdates für die v1-API, aber die Entwicklung neuer Funktionen konzentriert sich jetzt alle auf v2. Wie schreiben, testen und implementieren wir Änderungen auf unserem API-Server? Ich kann mindestens zwei Lösungen sehen:
Verwenden Sie einen Versionsverwaltungszweig / ein Tag für die v1-Codebasis. v1 und v2 werden unabhängig voneinander entwickelt und bereitgestellt, wobei bei Bedarf Revisionskontrollzusammenführungen verwendet werden, um denselben Bugfix auf beide Versionen anzuwenden - ähnlich wie Sie Codebasen für native Apps verwalten würden, wenn Sie eine neue Hauptversion entwickeln und gleichzeitig die vorherige Version unterstützen.
Machen Sie die Codebasis selbst auf die API-Versionen aufmerksam, sodass Sie eine einzige Codebasis erhalten, die sowohl die v1-Kundenrepräsentation als auch die v2-Kundenrepräsentation enthält. Behandeln Sie die Versionierung als Teil Ihrer Lösungsarchitektur anstelle eines Bereitstellungsproblems. Verwenden Sie möglicherweise eine Kombination aus Namespaces und Routing, um sicherzustellen, dass Anforderungen von der richtigen Version verarbeitet werden.
Der offensichtliche Vorteil des Zweigstellenmodells besteht darin, dass es trivial ist, alte API-Versionen zu löschen - beenden Sie einfach die Bereitstellung des entsprechenden Zweigs / Tags -, aber wenn Sie mehrere Versionen ausführen, kann dies zu einer wirklich komplizierten Zweigstruktur und Bereitstellungspipeline führen. Das "Unified Codebase" -Modell vermeidet dieses Problem, aber (glaube ich?) Würde es viel schwieriger machen, veraltete Ressourcen und Endpunkte aus der Codebasis zu entfernen, wenn sie nicht mehr benötigt werden. Ich weiß, dass dies wahrscheinlich subjektiv ist, da es wahrscheinlich keine einfache richtige Antwort gibt, aber ich bin gespannt, wie Organisationen, die komplexe APIs über mehrere Versionen hinweg verwalten, dieses Problem lösen.
quelle
Antworten:
Ich habe beide Strategien verwendet, die Sie erwähnen. Von diesen beiden favorisiere ich den zweiten Ansatz, der einfacher ist, in Anwendungsfällen, die ihn unterstützen. Das heißt, wenn die Versionsanforderungen einfach sind, wählen Sie ein einfacheres Software-Design:
Ich fand es nicht allzu schwierig, veraltete Versionen mit diesem Modell zu entfernen:
Der erste Ansatz ist unter dem Gesichtspunkt der Reduzierung von Konflikten zwischen gleichzeitig vorhandenen Versionen sicherlich einfacher, aber der Aufwand für die Wartung separater Systeme überwog tendenziell den Vorteil der Reduzierung von Versionskonflikten. Trotzdem war es kinderleicht, einen neuen öffentlichen API-Stack zu erstellen und mit der Iteration in einem separaten API-Zweig zu beginnen. Natürlich setzte der Generationsverlust fast sofort ein und die Zweige verwandelten sich in ein Durcheinander von Zusammenschlüssen, Zusammenführen von Konfliktlösungen und anderem solchen Spaß.
Ein dritter Ansatz betrifft die Architekturebene: Nehmen Sie eine Variante des Fassadenmusters an und abstrahieren Sie Ihre APIs in öffentlich zugängliche, versionierte Ebenen, die mit der entsprechenden Fassadeninstanz kommunizieren, die wiederum über ihre eigenen APIs mit dem Backend kommuniziert. Ihre Fassade (ich habe in meinem vorherigen Projekt einen Adapter verwendet) wird zu einem eigenen Paket, das in sich geschlossen und testbar ist und es Ihnen ermöglicht, Frontend-APIs unabhängig vom Backend und voneinander zu migrieren.
Dies funktioniert, wenn Ihre API-Versionen dazu neigen, dieselben Arten von Ressourcen verfügbar zu machen, jedoch mit unterschiedlichen strukturellen Darstellungen, wie in Ihrem Beispiel für vollständigen Namen / Vor- / Nachnamen. Es wird etwas schwieriger, wenn sie sich auf verschiedene Backend-Berechnungen verlassen, wie in "Mein Backend-Service hat falsch berechnete Zinseszinsen zurückgegeben, die in der öffentlichen API v1 verfügbar gemacht wurden. Unsere Kunden haben dieses falsche Verhalten bereits gepatcht. Daher kann ich das nicht aktualisieren." Berechnung im Backend und lassen Sie es bis v2 anwenden. Deshalb müssen wir jetzt unseren Zinsberechnungscode geben. " Glücklicherweise sind diese eher selten: In der Praxis bevorzugen Verbraucher von RESTful-APIs genaue Ressourcendarstellungen gegenüber der Abwärtskompatibilität von Fehler zu Fehler, selbst bei nicht bahnbrechenden Änderungen an einer theoretisch idempotenten
GET
Ressource.Ich werde interessiert sein, Ihre eventuelle Entscheidung zu hören.
quelle
Für mich ist der zweite Ansatz besser. Ich habe es für die SOAP-Webdienste verwendet und plane, es auch für REST zu verwenden.
Während Sie schreiben, sollte die Codebasis versionfähig sein, aber eine Kompatibilitätsebene kann als separate Ebene verwendet werden. In Ihrem Beispiel kann die Codebasis eine Ressourcendarstellung (JSON oder XML) mit Vor- und Nachnamen erstellen, die Kompatibilitätsebene ändert sie jedoch so, dass stattdessen nur der Name angezeigt wird.
Die Codebasis sollte nur die neueste Version implementieren, sagen wir v3. Die Kompatibilitätsschicht sollte die Anforderungen und Antworten zwischen der neuesten Version v3 und den unterstützten Versionen, z. B. v1 und v2, konvertieren. Die Kompatibilitätsschicht kann für jede unterstützte Version separate Adapter haben, die als Kette verbunden werden können.
Beispielsweise:
Client v1 Anfrage: v1 an v2 anpassen ---> v2 an v3 anpassen ----> Codebasis
Client v2-Anforderung: v1 an v2 anpassen (überspringen) ---> v2 an v3 anpassen ----> Codebasis
Für die Antwort arbeiten die Adapter einfach in die entgegengesetzte Richtung. Wenn Sie Java EE verwenden, können Sie beispielsweise die Servlet-Filterkette als Adapterkette verwenden.
Das Entfernen einer Version ist einfach. Löschen Sie den entsprechenden Adapter und den Testcode.
quelle
Die Verzweigung scheint mir viel besser zu sein, und ich habe diesen Ansatz in meinem Fall verwendet.
Ja, wie Sie bereits erwähnt haben - das Zurückportieren von Fehlerkorrekturen erfordert einige Anstrengungen, aber gleichzeitig erfordert die Unterstützung mehrerer Versionen unter einer Quellbasis (mit Routing und allen anderen Dingen) Sie, wenn nicht weniger, aber mindestens denselben Aufwand, um das System mehr zu machen kompliziert und monströs mit verschiedenen Zweigen der Logik im Inneren (irgendwann werden Sie definitiv zu einem großen
case()
Hinweis auf Versionsmodule kommen, deren Code dupliziert wurde oder noch schlimmer istif(version == 2) then...
). Vergessen Sie auch nicht, dass Sie für Regressionszwecke die Tests immer noch verzweigt halten müssen.In Bezug auf die Versionsrichtlinien: Ich würde maximal -2 Versionen von der aktuellen Version beibehalten und die Unterstützung für alte Versionen ablehnen - dies würde den Benutzern eine gewisse Motivation zum Umzug geben.
quelle
[Version(From="v1", To="v2")]
,[Version(From="v2", To="v3")]
,[Version(From="v1")] // All versions
es gerade jetzt zu erforschen, jemals jemand gehört es?Normalerweise ist die Einführung einer Hauptversion der API, die Sie in die Situation führt, dass mehrere Versionen verwaltet werden müssen, ein Ereignis, das nicht sehr häufig auftritt (oder nicht auftreten sollte). Es kann jedoch nicht vollständig vermieden werden. Ich denke, es ist insgesamt eine sichere Annahme, dass eine einmal eingeführte Hauptversion für einen relativ langen Zeitraum die neueste Version bleiben würde. Auf dieser Grundlage würde ich es vorziehen, den Code auf Kosten der Duplizierung zu vereinfachen, da ich dadurch sicherer bin, dass ich die vorherige Version nicht beschädige, wenn ich Änderungen an der neuesten Version vornehme.
quelle