GraphQL für Microservices: Schema Stitching vs. Federation vertieft
AI generated
{ }
type
GraphQL · Microservices · Federation · Architektur
GraphQL für Microservices
Schema Stitching gegen Apollo Federation im Detail

Wer mehrere Microservices unter einem einzigen GraphQL-Endpunkt vereinen will, steht vor der Wahl zwischen manuellem Schema Stitching und deklarativer Federation. Beide Ansätze lösen das Problem eines gemeinsamen Schemas grundlegend unterschiedlich, mit spürbaren Folgen für Team-Autonomie, Performance und Betriebsaufwand.

19 Min. Lesezeit Schema Stitching · Apollo Federation · Subgraphs · Gateway GraphQL 16 · Apollo Federation 2 · Microservices

1. Warum Microservices ein gemeinsames GraphQL-Schema brauchen

Sobald ein System in mehrere Microservices zerlegt wird, entsteht ein Widerspruch, den GraphQL für Microservices auflösen muss: Frontends wollen einen einzigen, konsistenten Graphen abfragen, während die Backends bewusst getrennt und unabhängig deploybar bleiben sollen. Ein Team, das Produktdaten verwaltet, ein anderes Bestellungen, ein drittes Kundendaten. Ohne eine übergreifende Schicht müssten Clients gegen drei separate Endpunkte arbeiten und die Verknüpfung der Daten selbst übernehmen. Genau diese Verknüpfung soll ein föderiertes GraphQL-Schema zentral und typsicher lösen.

Die Antwort auf diese Anforderung heißt in der Praxis meist Schema Stitching oder Federation, zwei fundamental unterschiedliche Wege, mehrere Teilschemata zu einem Gesamtschema zusammenzuführen. Beide Ansätze bei GraphQL für Microservices verfolgen dasselbe Ziel, ein einziges Query-Interface nach außen, verteilte Verantwortlichkeiten nach innen, unterscheiden sich aber stark in der Umsetzung, im Kopplungsgrad zwischen Gateway und Services und in der Frage, wie viel Governance ein Team dafür aufbauen muss. Die folgenden Abschnitte gehen beide Ansätze im Detail durch, mit konkreten Beispielen aus einer produktiven verteilten Architektur.

2. Schema Stitching: Funktionsweise und Grenzen

Schema Stitching war historisch der erste praktikable Weg, mehrere GraphQL-Schemata zu kombinieren. Ein zentraler Gateway-Prozess holt sich die Schemata aller beteiligten Services per Introspection oder aus statischen SDL-Dateien, führt sie in einem einzigen ausführbaren Schema zusammen und definiert an den Nahtstellen manuelle Delegationsregeln. Diese Regeln bestimmen, welcher Sub-Service für welches Feld tatsächlich angefragt wird und wie Ergebnisse aus mehreren Services zu einem Objekt zusammengeführt werden. Der Gateway trägt dabei die volle Verantwortung für die Verknüpfungslogik.

Der entscheidende Nachteil von Schema Stitching zeigt sich beim Skalieren: Jede neue Verknüpfung zwischen zwei Services muss im Gateway-Code explizit programmiert werden, meist über sogenannte Type Merging Config oder Remote Schema Transforms. Bei drei oder vier Services ist das überschaubar, bei zwanzig Services wird der Gateway selbst zum Monolithen, der bei jeder Schema-Änderung eines Teams neu deployt werden muss. Genau dieses Kopplungsproblem war der Ausgangspunkt für Apollo Federation, das dieselbe Aufgabe deklarativ statt imperativ löst.


# Schema Stitching: manual delegation config (conceptual, graphql-tools style)
# Subservice A: products
type Product {
  id: ID!
  sku: String!
  name: String!
}

# Subservice B: reviews
type Review {
  id: ID!
  productId: ID!
  rating: Int!
  text: String!
}

# Gateway must manually define how "reviews" hangs off "Product" -
# this merge logic lives OUTSIDE both schemas, in gateway config
extend type Product {
  reviews: [Review!]!
}

3. Apollo Federation: Subgraphs, Entities und der Supergraph

Apollo Federation dreht das Prinzip von Schema Stitching um: Statt dass ein zentraler Gateway die Verknüpfungslogik besitzt, deklariert jeder Subgraph selbst, welche Felder er zu einem gemeinsamen Typ beisteuert. Die Direktive @key markiert ein Feld als eindeutigen Identifikator einer Entity, die über mehrere Subgraphs verteilt sein darf. Der Gateway, in Apollo-Terminologie Router genannt, komponiert daraus zur Build-Zeit einen sogenannten Supergraph, eine einzige Schema-Datei, die beschreibt, welcher Subgraph welches Feld liefert.

Der große Vorteil bei GraphQL für Microservices mit Federation: Teams können ihre Subgraphs unabhängig deployen, ohne den Gateway anzufassen. Ein neues Feld an Product im Produkt-Team-Subgraph erscheint automatisch im Supergraph, sobald die Composition neu läuft, meist automatisiert in der CI-Pipeline über das Rover-Tooling. Diese Entkopplung ist der Hauptgrund, warum sich Federation in großen Organisationen mit vielen autonomen Teams gegenüber klassischem Schema Stitching durchgesetzt hat.


# Federation: Products subgraph declares the entity and its key
type Product @key(fields: "id") {
  id: ID!
  sku: String!
  name: String!
  price: Money!
}

# Federation: Reviews subgraph extends the SAME entity independently -
# no coordination with the Products team required at deploy time
type Product @key(fields: "id") {
  id: ID! @external
  reviews: [Review!]!
  averageRating: Float!
}

type Review {
  id: ID!
  rating: Int!
  text: String!
  author: String!
}

4. Entity Resolution und Reference Resolver im Detail

Das Herzstück jeder Federation-Implementierung ist der Reference Resolver, in Apollo-Terminologie __resolveReference. Wenn der Router eine Query bearbeitet, die Felder aus mehreren Subgraphs für dieselbe Entity kombiniert, sendet er zunächst eine Query an den Subgraph, der die Entity ursprünglich liefert, und danach eine _entities-Query an jeden weiteren Subgraph, der zusätzliche Felder beisteuert. Dieser zweite Aufruf übergibt nur den Key, in der Regel die ID, und der Subgraph muss daraus das vollständige Objekt für seinen eigenen Teil rekonstruieren.

Genau hier entstehen in der Praxis die meisten Performance-Probleme bei GraphQL für Microservices: Ein naiver Reference Resolver, der pro Entity einen einzelnen Datenbank-Query auslöst, produziert bei einer Liste von hundert Produkten hundert einzelne Anfragen an den Reviews-Service. Die Lösung ist derselbe DataLoader-Ansatz wie bei normalem N+1-Handling, nur dass der Batch-Punkt hier der _entities-Resolver ist, der alle angefragten Keys in einem Aufruf sammelt und als einzige Batch-Query an die Datenbank weiterreicht.


// Reviews subgraph: __resolveReference with batched DataLoader
// (webonyx/graphql-php style, simplified for clarity)
final class ProductEntityResolver
{
    public function __construct(
        private readonly ReviewBatchLoader $batchLoader,
    ) {
    }

    /**
     * Resolves the "reviews" and "averageRating" fields for a Product
     * entity reference coming from the federation gateway.
     *
     * @param array{id: string} $reference Entity reference with the @key fields
     * @return array{id: string, reviews: array, averageRating: float}
     */
    public function resolveReference(array $reference): array
    {
        // The batch loader collects all reference IDs from one gateway
        // round-trip and issues a single SQL query, not one per product.
        $reviews = $this->batchLoader->load($reference['id']);

        return [
            'id' => $reference['id'],
            'reviews' => $reviews,
            'averageRating' => $this->average($reviews),
        ];
    }

    private function average(array $reviews): float
    {
        if ($reviews === []) {
            return 0.0;
        }

        $sum = array_sum(array_column($reviews, 'rating'));

        return round($sum / count($reviews), 2);
    }
}

5. Schema Ownership und Team-Grenzen in föderierten Systemen

Federation zwingt Teams zu einer Entscheidung, die bei einem monolithischen Schema nie gestellt werden muss: Wer besitzt welches Feld eines gemeinsamen Typs? Das Produkt-Team besitzt Product.name und Product.price, das Reviews-Team besitzt Product.reviews und Product.averageRating. Diese Aufteilung sollte sich an fachlichen Bounded Contexts orientieren, nicht an technischen Zufälligkeiten, sonst entstehen Subgraphs, die aus organisatorischer Bequemlichkeit geschnitten sind statt aus fachlicher Kohärenz.

In der Praxis lohnt sich bei GraphQL für Microservices eine schriftliche Schema-Ownership-Matrix, die jeden Typ und jedes strittige Feld einem Team zuordnet, kombiniert mit CODEOWNERS-Dateien im jeweiligen Subgraph-Repository. Konflikte entstehen typischerweise bei Feldern, die mehrere Teams gleichermaßen betreffen, etwa ein Status-Feld, das sowohl Bestell- als auch Versandlogik beeinflusst. Hier hilft die Federation-Direktive @shareable, die explizit erlaubt, dass mehrere Subgraphs dasselbe Feld liefern dürfen, solange sie identische Werte zurückgeben.

6. Query Planning und Performance im Federation-Gateway

Bevor der Router eine eingehende Query beantwortet, erstellt er einen Query Plan, eine Sequenz von Teilanfragen an die beteiligten Subgraphs, optimiert für minimale Round-Trips. Felder, die aus demselben Subgraph stammen, werden zu einer einzigen Anfrage zusammengefasst, Felder aus verschiedenen Subgraphs werden parallel statt sequenziell angefragt, wo immer die Abhängigkeitsstruktur das zulässt. Dieser Plan lässt sich mit dem Apollo Router im Debug-Modus als JSON-Struktur ausgeben und zeigt sofort, wo unerwartete sequenzielle Aufrufe entstehen.

Ein häufiges Performance-Problem bei GraphQL für Microservices ist tiefe Verschachtelung über mehrere Subgraphs hinweg: Eine Query, die Produkte, deren Reviews und die Autoren dieser Reviews aus drei unterschiedlichen Services abfragt, erzeugt einen Query Plan mit drei sequenziellen Stufen, da jede Stufe die Keys der vorherigen benötigt. Response Caching auf Subgraph-Ebene und aggressive Batch-Größen im Router reduzieren diese Latenz spürbar, ändern aber nichts an der grundsätzlichen Stufenzahl, die durch die Schemastruktur vorgegeben ist.


# Rover CLI: compose a supergraph from multiple subgraph schemas
# and publish it to the schema registry
rover subgraph check my-graph@production \
  --schema ./products/schema.graphql \
  --name products

rover supergraph compose \
  --config ./supergraph-config.yaml \
  --output ./supergraph.graphql

rover subgraph publish my-graph@production \
  --schema ./products/schema.graphql \
  --name products \
  --routing-url https://products.internal/graphql

7. Versionierung und Schema-Evolution über Team-Grenzen hinweg

Ein föderiertes Schema ändert sich ständig, weil unabhängige Teams unabhängig deployen. Genau deshalb ist Schema-Evolution bei GraphQL für Microservices heikler als in einem einzelnen Service: Ein Breaking Change in einem Subgraph kann Clients treffen, die über den Gateway auf Felder eines ganz anderen Teams zugreifen, ohne dass das verursachende Team davon weiß. Composition Checks in der CI-Pipeline, die jede vorgeschlagene Subgraph-Änderung gegen tatsächliche Query-Traffic-Muster validieren, sind hier keine Kür, sondern Grundvoraussetzung für sicheres, unabhängiges Deployment.

Das Schema Registry-Konzept, wie es Apollo GraphOS oder Alternativen wie Hive anbieten, speichert nicht nur die aktuelle Version jedes Subgraphs, sondern auch, welche Felder tatsächlich von Clients genutzt werden. Ein als Breaking Check markiertes Feld, das laut Traffic-Analyse seit Monaten niemand mehr abfragt, kann entfernt werden, ohne den Composition Check zu blockieren. Diese Datenbasis verhindert, dass Teams aus reiner Vorsicht jahrelang tote Felder mitschleppen.

8. Monitoring und Debugging föderierter GraphQL-Systeme

Ein einzelner fehlerhafter Request bei GraphQL für Microservices kann durch drei, vier oder fünf Subgraphs wandern, bevor der Router eine Antwort zusammensetzt. Ohne durchgängiges Distributed Tracing, das eine Trace-ID vom eingehenden Request über jeden Subgraph-Aufruf hinweg propagiert, ist die Fehlersuche in einer föderierten Architektur praktisch aussichtslos. Der Apollo Router unterstützt OpenTelemetry nativ und exportiert Spans für jede Phase, Parsing, Validation, Query Planning und jede einzelne Subgraph-Anfrage.

Ein zweiter, oft übersehener Aspekt ist die Fehlerpropagation: Schlägt ein Subgraph fehl, während andere Teile der Query erfolgreich beantwortet wurden, liefert GraphQL standardkonform eine partielle Response mit errors-Array und null an der betroffenen Stelle. Clients müssen darauf vorbereitet sein, partielle Daten korrekt darzustellen statt die gesamte Antwort zu verwerfen, sonst wirkt ein einzelner Subgraph-Ausfall so, als sei das komplette System down, obwohl neunzig Prozent der Daten valide angekommen sind.

9. Schema Stitching vs. Federation im direkten Vergleich

Beide Ansätze lösen dieselbe Grundaufgabe, unterscheiden sich aber in Kopplungsgrad, Betriebsaufwand und Team-Autonomie erheblich. Die folgende Tabelle fasst die wichtigsten Entscheidungskriterien für GraphQL für Microservices zusammen.

Kriterium Schema Stitching Apollo Federation
Verknüpfungslogik Zentral im Gateway, manuell programmiert Dezentral je Subgraph, deklarativ per @key
Team-Autonomie Gering, Gateway-Team als Flaschenhals Hoch, unabhängige Deploys je Subgraph
Skalierung auf viele Services Gateway wird selbst zum Monolithen Linear, Composition automatisiert
Einstiegshürde Niedrig, keine neue Direktiven-Syntax Höher, @key/@shareable/@external lernen
Tooling-Ökosystem Klein, meist graphql-tools Groß, Rover, GraphOS, Router, Hive

Für kleine Systeme mit zwei oder drei Services und einem einzigen Gateway-Team bleibt Schema Stitching ein pragmatischer, schnell umsetzbarer Weg. Sobald jedoch mehrere autonome Teams unabhängig deployen wollen, was der eigentliche Zweck einer Microservices-Architektur ist, überwiegen die strukturellen Vorteile von Federation deutlich, trotz der höheren initialen Lernkurve.

Mironsoft

GraphQL-Architektur, Microservices und Magento-Integration

Ein gemeinsames GraphQL-Schema für eure Microservices?

Wir analysieren eure bestehende Service-Landschaft, entwerfen einen Subgraph-Schnitt entlang fachlicher Grenzen und begleiten die Einführung von Apollo Federation von der ersten Entity bis zum produktiven Router.

Architektur-Review

Schema-Ownership-Matrix und Subgraph-Schnitt entlang fachlicher Bounded Contexts

Federation-Einführung

Migration von Schema Stitching oder REST auf Apollo Federation mit Router-Setup

Performance-Tuning

DataLoader-Batching in Reference Resolvern und Query-Plan-Optimierung

10. Zusammenfassung

GraphQL für Microservices braucht immer eine Föderationsstrategie, sobald mehr als ein Team an einem gemeinsamen Schema arbeitet. Schema Stitching löst das Problem zentral und manuell, mit einem Gateway, der bei wachsender Service-Zahl selbst zum Engpass wird. Apollo Federation verlagert die Verknüpfungslogik dezentral in jeden Subgraph, über die @key-Direktive und Reference Resolver, und ermöglicht dadurch echte Team-Autonomie mit unabhängigen Deployments.

Der Umstieg lohnt sich vor allem dann, wenn die Zahl der beteiligten Teams wächst und die Kopplung durch einen zentralen Gateway zum spürbaren Bremsklotz wird. Wer neu startet und absehbar mehr als drei oder vier Services föderieren muss, sollte direkt mit Federation beginnen, denn der nachträgliche Umstieg von Schema Stitching auf Federation bedeutet in der Praxis eine vollständige Neuimplementierung der Verknüpfungslogik.

GraphQL für Microservices — Das Wichtigste auf einen Blick

Schema Stitching

Zentraler Gateway mit manueller Delegationslogik. Praktikabel für zwei bis drei Services, wird bei mehr Services schnell selbst zum Monolithen.

Apollo Federation

Dezentrale Verknüpfung über @key-Entities. Jeder Subgraph liefert eigene Felder, der Router komponiert automatisch den Supergraph.

Reference Resolver

__resolveReference muss gebatcht werden, sonst entsteht N+1 auf Entity-Ebene bei jeder Liste über mehrere Subgraphs.

Schema Ownership

Klare Feld-zu-Team-Zuordnung und Composition Checks in der CI-Pipeline sind Pflicht, nicht optional.

11. FAQ: GraphQL für Microservices

1Grundlegender Unterschied Stitching vs. Federation?
Stitching verwaltet Verknüpfung zentral im Gateway, Federation dezentral in jedem Subgraph über @key. Federation erlaubt unabhängige Deploys je Team.
2Ab wann lohnt sich Federation?
Ab vier bis fünf unabhängigen Teams mit eigenen Deploy-Zyklen. Bei zwei bis drei Services reicht oft Schema Stitching.
3Was macht @key?
Markiert das eindeutige Identifikationsfeld einer Entity. Mehrere Subgraphs können den Typ mit demselben @key eigenständig erweitern.
4N+1 in Reference Resolvern vermeiden?
Der Router batcht alle Referenzen in einem _entities-Aufruf. Der Subgraph muss diesen mit einem DataLoader statt Einzelqueries verarbeiten.
5Was passiert bei Subgraph-Ausfall?
Eine partielle Response mit errors-Array und null an der betroffenen Stelle. Andere Felder werden normal beantwortet.
6Feld in mehreren Subgraphs erlaubt?
Ja, mit @shareable. Ohne diese Direktive meldet die Composition bei doppelten Feldern einen Konflikt.
7Query Plan analysieren?
Der Apollo Router gibt im Debug-Modus den Query Plan als JSON aus, sichtbar wird Reihenfolge und Parallelität der Subgraph-Aufrufe.
8Was ist eine Schema Registry?
Zentrale Verwaltung von Subgraph-Versionen und tatsächlicher Feldnutzung, Grundlage für Composition Checks vor jedem Deploy.
9Migration von Stitching zu Federation?
Vollständige Neuimplementierung der Verknüpfungslogik: aus Type-Merging-Configs werden @key-Direktiven und Reference Resolver je Subgraph.
10Nötiges Tooling im Produktivbetrieb?
Rover für Composition, ein Apollo Router, und eine Schema Registry mit Composition Checks in der CI-Pipeline vor jedem Merge.