backend / graphql
Fédération de schémas
Explication
Ce que vous allez apprendre
- Comprendre la limite d'un schéma GraphQL monolithique à l'échelle de plusieurs équipes
- Déclarer une clé d'entité avec
@keypour permettre à un service de la référencer - Étendre un type possédé par un autre service avec
extend typeet@external - Comprendre le rôle du gateway et du
resolve_referencelors d'une requête traversant plusieurs services - Distinguer la fédération Apollo du schema stitching, son approche historique concurrente
Dans quel contexte ?
Une entreprise de taille moyenne a une équipe "produits" et une équipe "avis clients", chacune avec son propre service backend et sa propre base de données. L'équipe avis veut ajouter un champ avis directement sur le type Produit, sans dépendre du déploiement de l'équipe produits ni dupliquer sa base de données. La fédération de schémas permet à chaque équipe de posséder et déployer sa partie du schéma de façon complètement indépendante.
D'abord, le problème d'un schéma unique à plusieurs équipes
Un schéma GraphQL monolithique, où tout le code vit dans un seul service et un seul dépôt, devient un point de contention dès que plusieurs équipes doivent le faire évoluer en parallèle : chaque déploiement doit coordonner tout le monde, et un bug dans le module d'une équipe peut faire tomber l'API entière.
Une fois ce problème identifié, l'idée de la fédération devient logique
Plutôt qu'un seul schéma géant, chaque service expose son propre "sous-graphe" GraphQL, responsable d'une partie du schéma global. Un gateway (une couche supplémentaire, spécifique à la fédération) interroge ces sous-graphes en coulisses et compose une réponse unique, comme si tout venait d'un seul serveur — le client ne voit jamais la complexité derrière.
Ensuite, comment un service référence-t-il un type qu'il ne possède pas ?
C'est le rôle de la directive @key(fields: "id") : elle déclare qu'un type peut être identifié de façon unique par ce champ, à travers plusieurs services. Le service "avis" peut alors écrire extend type Produit @key(fields: "id") { avis: [Avis!]! } pour ajouter un champ à un type qu'il ne possède pas lui-même, en le référençant uniquement par sa clé.
| Concept fédération | Rôle |
|---|---|
@key(fields: "id") | Déclare la clé qui identifie une entité à travers les services |
extend type | Ajoute des champs à un type possédé par un autre service |
@external | Marque un champ comme venant d'un autre service, non résolu localement |
resolve_reference | Reconstruit une entité locale à partir de la clé fournie par le gateway |
Il reste un mécanisme technique à comprendre : comment le gateway "recompose" une entité ?
Quand une requête demande produit { nom avis { note } }, le gateway interroge d'abord le service "produits" pour nom, obtient la clé id du produit, puis appelle le service "avis" avec cette clé pour résoudre avis. La fonction resolve_reference, côté service "avis", reçoit cette clé et reconstruit un objet Produit partiel (juste assez pour résoudre ses propres champs) sans jamais avoir besoin d'accéder à la base de données de l'équipe "produits".
Prérequis
Cette leçon suppose une bonne maîtrise des types et resolvers (leçons 2 et 5) : la fédération ajoute une couche de coordination entre services, mais chaque sous-graphe reste un schéma GraphQL classique.
Piège fréquent
Confondre la fédération avec le schema stitching (une approche plus ancienne et plus fragile, où la propriété des types reste ambiguë et la résolution des clés est manuelle) mène à sous-estimer la complexité d'une migration : la fédération standardise ce que le stitching laissait à l'improvisation de chaque équipe.
Ce cours se termine sur la performance à très grande échelle : la dernière leçon rassemble DataLoader, cache Redis, persisted queries et tracing dans une checklist de production niveau expert.
Commandes & code
Fédération de schémas
# Sans fédération : un seul schéma monolithique devient difficile à faire évoluer à plusieurs équipes
# Avec fédération (Apollo Federation) : chaque service possède un sous-graphe, un gateway les compose
# --- Service "produits" ---
type Produit @key(fields: "id") {
id: ID!
nom: String!
prix: Float!
}
# --- Service "avis" : étend Produit depuis un service indépendant, sans le posséder ---
extend type Produit @key(fields: "id") {
id: ID! @external
avis: [Avis!]!
}
type Avis {
id: ID!
note: Int!
commentaire: String!
}# Service "avis" : résout uniquement les champs qu'il possède, en utilisant la clé fournie
import strawberry
from strawberry.federation import Schema
@strawberry.federation.type(keys=["id"])
class Produit:
id: strawberry.ID
@strawberry.field
def avis(self) -> list["Avis"]:
return avis_service.lister_pour_produit(self.id)
# Reference resolver : reconstruit un Produit à partir de la clé fournie par le gateway
@classmethod
def resolve_reference(cls, id: strawberry.ID) -> "Produit":
return Produit(id=id)
schema = Schema(query=Query, types=[Produit])# Gateway Apollo : agrège les sous-graphes en un schéma unique côté client
# gateway.config.yaml (conceptuel)
subgraphs:
- name: produits
url: http://service-produits:4001/graphql
- name: avis
url: http://service-avis:4002/graphql
- name: utilisateurs
url: http://service-utilisateurs:4003/graphql# Côté client : une seule requête traverse plusieurs services de manière transparente
query {
produit(id: "42") {
nom # résolu par le service "produits"
avis { # résolu par le service "avis", jointure faite par le gateway
note
commentaire
}
}
}Fédération vs schema stitching (approche historique) :
| Aspect | Schema stitching | Apollo Federation |
|----------------------|-------------------------------------|--------------------------------------|
| Propriété des types | Ambiguë, souvent dupliquée | Chaque champ appartient à UN service |
| Résolution des clés | Manuelle, fragile | @key standardisé, reference resolver |
| Évolution indépendante| Difficile (couplage fort) | Chaque équipe déploie son sous-graphe|Résumé
@keydéclare la clé permettant au gateway de recomposer un type réparti sur plusieurs services.extend typepermet à un service d'ajouter des champs à un type qu'il ne possède pas lui-même.- Le
resolve_referencereconstruit une entité à partir de sa clé quand le gateway traverse les sous-graphes. - La fédération permet à plusieurs équipes de posséder et déployer leur sous-graphe indépendamment.
Exercices pratiques
Mission : ajouter les avis clients sans toucher au service produits
Objectif : Concevoir l'extension fédérée du type Produit depuis le service avis, sans dépendre du déploiement ni de la base de données de l'équipe produits.
Contexte
L'équipe "avis" veut exposer un champ avis directement sur le type Produit, qui appartient entièrement à l'équipe "produits" (autre dépôt, autre base de données, autre cycle de déploiement). Une réunion inter-équipes vient de valider l'usage de la fédération Apollo plutôt qu'un schema stitching maison, jugé trop fragile pour ce cas.