Über unsMediaKontaktImpressum
Oliver Jumpertz & Patrick Steinert 26. Juli 2022

Federated GraphQL in Microservice-Umgebungen

Eine Modernisierung der Software-Architektur von RTL+

GraphQL Federation ist eine Möglichkeit, um mit vielen Teams an einer Unified-API in einer Microservice-Umgebung zu entwickeln. Der Erfahrungsbericht am Beispiel des Streamingdienstes RTL+ zeigt, wie man 20 Frontends über eine API versorgt.

Die Modernisierung: wie alles begann

"Es lief lange gut, aber irgendwann war es nicht mehr praktikabel": mit dieser oder ähnlichen Aussagen starten viele Veränderungsprojekte. Es kommt der Zeitpunkt, an dem sich die Chance ergibt, etwas zu verändern. So war es auch in der Architektur des TV-Streamingdienstes TVNOW (heute RTL+), die es in einem gemeinsamen Projekt zu modernisieren galt.

Zuschauer können über 20 Client-Plattformen (u. a. Smartphone, Smart-TV, HBBTV, Spielekonsolen, MagentaTV) Serien, Filme, Live-Events und Live-Sport-Übertragungen streamen. In der Architektur wurde für die Client-Plattformen, also die Frontends, jeweils ein Backend nach dem Backend-for-Frontend-Pattern (BFF) erstellt. Anfangs waren es wenige Clients und so konnten für Web und Mobile individuell zugeschnittene BFFs die Frontends optimal bedienen. Bei einer Microservice-Architektur mit vielen Microservices ist es sonst sehr schwierig, auf die Frontends zugeschnittene Schnittstellen zu entwickeln. Zudem müssen die Frontends mit vielen Services kommunizieren.

Mit der Zeit wurden es jedoch mehr und mehr Clients, wodurch sich die BFFs vervielfachten. Dadurch wurden für ein neues Feature viele mehrfache Code-Änderungen durchgeführt – ein klarer Nachteil von BFFs bei vielen Clients.

Durch die Neuausrichtung zu RTL+ brauchte es eine flexiblere Lösung

Viele neue Funktionen sollten für das neue RTL+ integriert werden und es war klar, dass diese zu vielen Changes in den BFFs führen würden. So begann die Suche nach alternativen Ansätzen. Eine generalisierte REST-API war eine Lösungsidee, die aber zu unflexibel und nicht auf die Bedürfnisse des Clients zugeschnitten ist. Es gab einen weiteren Kandidaten, eine junge, frische und vielversprechende Technologie: GraphQL.

Was ist GraphQL?

GraphQL ist eine Schnittstellentechnologie zur Abfrage, Anlage und Änderung von Daten. Sie ist HTTP-basiert und eignet sich gut für Webservices. GraphQL erlaubt im Vergleich zu Rest, dass der Client bei der Anfrage spezifiziert, welche Daten benötigt werden. Zudem ermöglicht GraphQL es, komplexere Datenstrukturen incl. Beziehungen abzufragen (ähnlich zu SQL). Dadurch ergeben sich Implikationen für übliche Cachingverfahren.

Die Vorzüge von GraphQL lagen auf der Hand:

  • Eine client-orientierte Schnittstelle,
  • Flexibilität und Wiederverwendbarkeit durch anpassbare Requests und
  • Selbstdokumentation durch das Schema.

Nur die Ungewissheit war groß: Kann GraphQL die benötigte Performance liefern? Können wir an einer so zentralen Stelle auf so eine junge Technologie setzen? Es gab nur wenige Kenntnisse zu GraphQL im Unternehmen. Auf GraphQL zu setzen war also durchaus eine mutige Entscheidung. Man könnte sagen, der Schmerz war groß genug.

Erste Schritte: der Orchestration Layer

Im ersten Ansatz wurde ein Team geformt, das eine zentrale Orchestrierung vieler Backend-Services durchgeführt hat. Dazu wurden viele Transformatoren (auch Resolver genannt) entwickelt, die die REST-Interfaces der Microservices angesprochen haben. Dadurch konnte die erste graphQL-basierte API realisiert werden, die von allen Clients verwendet wurde. Erfolgreich war auch die Reduktion der HTTP-Requests für einen Seitenaufbau, wie Abb. 2 zeigt.

Es stellte sich jedoch schnell heraus, dass das eine Team sehr viel Wissen aus der Fachdomäne haben muss, um alle Bereiche gut abzudecken. Dies ist kein wünschenswerter Zustand, da sehr viel Wissen zentralisiert ist. Dies hat sich auch in der Effizienz der Entwicklung negativ dargestellt: Das Team musste zum Ende eines Program Increments (PI) viele Änderungen der anderen Teams nachziehen und entwickelte sich so zum Flaschenhals [1]. Zudem konnten Wünsche von den Frontends nicht zeitgerecht umgesetzt werden, da dazu eine Abstimmung und Priorisierung mit den Upstream-Microservices notwendig war. Ebenso konnten die Feature-Teams kaum Änderungen ohne das Orchestration-Team durchführen.

GraphQL Federation

Mit GraphQL-Federation kann eine einzige GraphQL-API oder ein Gateway alle GraphQL-APIs in einer Microservice-Umgebung orchestrieren. Der so entstehende Supergraph kann bei der Abfrage auch Verknüpfungen zwischen den einzelnen GraphQL-APIs der Microservices (Subgraphen) auflösen. Das Frontend hat dadurch nur einen Endpunkt, über den es alle Daten beziehen und manipulieren kann.

Ein neuer Weg: alles neu mit Federation

Verteilte Entwicklung an einer generalisierten API. Dazu musste der geschaffene Graph in einen Supergraph mit einem Subgraphen umgebaut werden. Dieser Schritt ist noch recht einfach. Danach wurden aus dem monolithischen Subgraphen einzelne Teile in eigenständige Subgraphen extrahiert. Diese wurden dann übernommen und können selbständig gepflegt werden. Dieses Vorgehen kann als Refactoring-Pattern beschrieben werden.

Refactoring-Anleitung (nach Fowler):

  1. Graph in Subgraph umwandeln und in Schema Registry anmelden.
  2. Allumfassende Subgraphen in einzelne, eigenständige Codeprojekte zerteilen (Bounded Contexts bzw. 1 je Microservice).
  3. Subgraphen-Projekte an Teams übergeben. Diese können dann eigene Implementierung oder Tool verwenden.

Erst dann wieder Schema-Changes machen.

Für die Federation mussten auch die bestehenden Teams lernen, wie sie gute Subgraphen erstellen und pflegen können. Aus den Erfahrungen heraus können mehrere Erfolgsfaktoren beschrieben werden.

Erfolgsfaktor Tooling

Die bereits erwähnte initiale API (nun ein Subgraph) wurde mit Node.js und den Apollo-Bibliotheken entwickelt [2]. Dies sorgte für kurze Entwicklungszyklen und annehmbare Performance durch die I/O-lastige Natur des Services, der als Proxy für andere Backendservices fungierte.

Mit der Einführung von Apollo Federation und der damit verbundenen Auftrennung des ehemaligen Monolithen wurde die Landschaft schließlich polyglotter und bunter. Teams, die sich im Node.js-Umfeld wohlfühlten, konnten von der bisher gesammelten Expertise profitieren. Andere entschlossen sich für das von Netflix entwickelte DGS [3], wieder andere entschlossen sich, GraphQL Kotlin von Expedia zu benutzen [4]. Dabei wurden die bisherigen REST-Layer vollständig durch GraphQL ersetzt. Die Backendservices gingen also durch eine Evolution vom eher backend-getriebenen Ansatz zu einem voll client-getriebenen Ansatz. Kein großes Problem, wenn man bedenkt, dass für Service-zu-Service-Kommunikation im konkreten Fall keine Anforderungen bestehen. Das Routing innerhalb der Federated-API übernimmt ein sogenanntes Gateway, eine Komponente, die von Apollo in JavaScript implementiert und mit Node.js ausgeführt wird [5].

Alle von einzelnen Teams gewählte Technologien mussten die Spezifikation von Apollo Federation unterstützen können. Da sich diese jedoch mittlerweile zum Quasi-Standard entwickelt hat, fällt es nicht schwer, für beinahe jede Sprache ein passendes Framework zu finden. Das lässt den einzelnen Teams viel Freiraum, um mit den Technologien zu arbeiten, mit denen sie sich am wohlsten fühlen und am produktivsten arbeiten können.

Tools für Schema-Management

  • Apollo Studio + Rover
  • Tyk (Cloud-Native-API-Management)
  • Selfmade

Erfolgsfaktor Schema-Management

Wenn aus vielen einzelnen Services, die alle eine API bereitstellen, auf einmal eine ziemlich große API entstehen soll, stehen Softwareentwickler:innen erst einmal vor der großen Frage, wie genau das gelingen kann. Glücklicherweise gibt es bereits mehrere Unternehmen, die sich dieselbe Frage stellten und eigene Lösungen dafür entwickelt haben.

In diesem Fall war die Wahl tatsächlich einfacher als zu Anfang vermutet. Der API-Monolith und die Frontends nutzten bereits Bibliotheken von Apollo und ganz nebenbei bietet das Unternehmen Apollo zufälligerweise auch noch ein Produkt an, das es einfacher macht, eine Federated-GraphQL-API zu bauen. Dieses Produkt, genannt Apollo Studio, war mit seinem Managed Federation simpel genug, in der Integration den Zuschlag zu erhalten [6]. Es musste lediglich ein Gateway aufgesetzt und ein Abonnement von Apollo Studio abgeschlossen werden. Ein paar Klicks im Frontend von Apollo Studio und das Deployment eines Gateways später stand der Prototyp einer Federated-API auch schon.

Das Prinzip ist an sich simpel. Jedes Subgraph-Team lädt sein Schema bei Apollo Studio hoch. Dort kümmert sich eine Schema-Registry darum, das Schema zu verwalten, ein Supergraph-Schema zusammenzusetzen und das Gateway mit diesem zu versorgen. Wenn dann alles zueinander passt, steht Frontend-Anfragen nichts mehr im Weg.

Allerdings besteht ein großer Unterschied darin, ob 5 Teams jeweils eine eigene API designen und umsetzen oder ob diese 5 Teams gemeinsam für ein einzelnes großes Schema verantwortlich sind. Änderungen, die zuvor eine unangenehme Zusatzaufgabe für ein Backend for Frontend oder einzelne Frontend-Entwickler waren, können auf einmal Einfluss auf ein gesamtes System nehmen. Zudem dreht sich jetzt zusätzlich noch die gesamte Welt um die Frontends.

Die Zeiten, in denen Änderungen einfach so gemacht werden konnten, waren schnell vorbei. Da es plötzlich keine Zwischenschicht mehr gab, waren die Backend-Teams für die Sicherstellung der Funktionalität bei Schema-Änderungen verantwortlich. Die Änderung, um dieses eine unnötige Feld unbedingt loswerden, kann dann das System in den Abgrund reißen.

Erfolgsfaktor Fehleranalyse

In einem verteilten System ist die Fehlersuche aufgrund der Service-Kommunikation bereits schwierig. Wenn mehrere Teams an einer zentralen Komponente arbeiten und gemeinsam die Verantwortung tragen, wird es nicht einfacher. Entsprechend sind gute Werkzeuge zur Transparenz sowie Tracing und Logging sehr wichtig. Durch Trace-Informationen (z. B. Trace ID) in den GraphQL-Responses können die Kommunikationswege in Tools wie jaeger aufgedeckt werden [7]. Apollo selbst bietet mit dem Apollo Studio eine umfangreiche Möglichkeit zur Überwachung des Graphen durch Metriken und Analysen von Error-Rate und Latenzzeiten an. Ein Detail ist dabei sehr hilfreich: die Frontend-Teams sollten einheitliche Operation-Names in den GraphQL-Queries verwenden. Ist das nicht der Fall, fällt es schwer, die Übersicht zu behalten.

Erfolgsfaktor Community

REST ist wohl immer noch die überwiegende Schnittstellentechnologie in Microservice-Architekturen. Entsprechend ist das Know-how zu GraphQL eher spärlich vorhanden. Für eine schnelle Adaption und gute Schnittstellen ist es sinnvoll, Ressourcen in Form von Tutorials, Best Practices und Patterns bereitzustellen [8]. Dazu lohnt es sich, eine Community of Practices aufzubauen und das Wissen aktiv zu vermitteln. Dies wurde durch das erste Team, das die ersten Erfahrungen gemacht hat, durchgeführt.

Dennoch zeigt sich, dass ein Shift der Mentalität, auch mit einer Community, Zeit benötigt. Es gibt immer wieder Teams, die ihre Änderungen am liebsten isoliert vorantreiben würden, muss man doch nun für jedes Feature potenziell auch Absprachen mit vielen anderen Kollegen treffen. Diese Änderung an der Arbeitsweise benötigt Zeit und einen demokratischen Prozess.

Haben die Teams verinnerlicht, dass nur alle gemeinsam eine erfolgreiche GraphQL-API gestalten und implementieren können und dass auch sie die Leidtragenden von schnellen Änderungen sein könnten, werden viele Entwickler:innen nachdenklich. In einem permanenten Prozess von Absprachen und Meet-ups muss immer wieder auf die gemeinschaftliche Verantwortung hingewiesen werden. Wenn Änderungen von allen Beteiligten überprüft und abgesegnet werden, wird sichergestellt, dass auch alle Sorgen und Ängste Gehör finden. Oftmals reicht dann ein demokratischer Mehrheitsentscheid aus, um Änderungen zu genehmigen oder abzulehnen – entsprechenden Raum für Diskussionen vorausgesetzt.

Der Showdown: so weit ist RTL+ gekommen

Hinter den vorgenannten Erfolgsfaktoren steckt ein längerer Weg. Insgesamt ist die Evolution von BFFs zur GraphQL Federation für RTL+ eine Erfolgsgeschichte. Trotz einiger Herausforderungen und anfänglicher Schmerzen konnten die mit der Veränderung verknüpften Ziele erreicht werden. Viele Teams arbeiten nun gemeinsam an einer globalen API, die sich aus vielen kleineren Teilen zusammensetzt. Features können mittlerweile ohne Umwege sofort von den jeweiligen Teams umgesetzt werden, das spart bis zu 30 Prozent Zeit ein. Es benötigt keine versetzten Sprints mehr, damit ein Backend for Frontend oder ein API-Monolith die entsprechenden Änderungen für die Frontends nachziehen kann.

Mittlerweile ist die API vollständig föderiert. Über funktionale Tests und Lasttests wird das Verhalten der API beobachtet und Auffälligkeiten direkt behandelt. Somit wird ein funktionierendes System sichergestellt. Die eingehende Frage, ob GraphQL die Performance liefern kann, kann demnach mit "Ja" beantwortet werden.

Durch die Dezentralisierung können die Teams autonom arbeiten und auch wenn sie sich direkt mit mehreren Frontend-Teams abstimmen müssen, wird dies als förderlich für die Schnittstellen und Entwicklungsgeschwindigkeit beurteilt.

Die Frontends haben indessen eine selbst dokumentierte API und brauchen in der Tat weniger Backend-Anfragen. Außerdem profitieren auch sie von der schnelleren Einführung von API-Features, die nun von den Backend-Teams selbst umgesetzt und bereitgestellt werden können. Für Teams, die zuvor ihr eigenes Backend for Frontend entwickeln, releasen und betreiben mussten, stellt das eine zusätzliche Entlastung dar, da der Fokus jetzt voll auf dem Frontend liegen kann. Sie müssen daneben nicht noch Backend-Releases durchführen. Auf der anderen Seite ermöglicht der vorgestellte Ansatz auch vertikale Feature-Teams, die Funktionen über die API einbringen können, die dann wiederum von anderen Teams wiederverwendet werden können. Somit wird die Ende-zu-Ende-Verantwortung von Teams unterstützt.

Auch wenn die initiale Einführung von GraphQL mittlerweile abgeschlossen ist, bleibt der Prozess, der konstante Änderungen erlaubt und gutheißt, weiterhin sehr lebendig. API-Erweiterungen können genauso schnell eingeführt werden, wie sie auch getestet werden können. Dank verschiedener Umgebungen können auch temporäre Tests schnell erfolgen und wieder verschwinden.

Insgesamt lässt sich festhalten, dass auch in einer großen Organisation wie der hier vorliegenden eine Federated-GraphQL-API in einer Microservice-Umgebung gut umsetzbar und profitabel für alle Beteiligten ist.

Autoren

Oliver Jumpertz

Oliver Jumpertz sammelte vor 22 Jahren seine ersten Erfahrungen mit dem Programmieren und entwickelt seit 9 Jahren in der Industrie Produkte mit altbewährten und neuen Technologien.
>> Weiterlesen

Patrick Steinert

Patrick Steinert entwickelt seit mehr als zehn Jahren neue Produkte mit innovativen Technologien im Web und IoT-Umfeld. Er ist Head of Technology Consulting bei der tarent solutions GmbH.
>> Weiterlesen
Das könnte Sie auch interessieren
Kommentare (0)

Neuen Kommentar schreiben