Sblocca la potenza di GraphQL Federation con Schema Stitching. Scopri come costruire un'API GraphQL unificata da più servizi, migliorando scalabilità e manutenibilità.
GraphQL Federation: Schema Stitching - Una Guida Completa
Nel panorama in continua evoluzione dello sviluppo di applicazioni moderne, la necessità di architetture scalabili e manutenibili è diventata fondamentale. I microservizi, con la loro inerente modularità e la capacità di essere distribuiti in modo indipendente, sono emersi come una soluzione popolare. Tuttavia, la gestione di numerosi microservizi può introdurre complessità, specialmente quando si tratta di esporre un'API unificata alle applicazioni client. È qui che entra in gioco GraphQL Federation, e in particolare Schema Stitching.
Cos'è GraphQL Federation?
GraphQL Federation è un'architettura potente che ti consente di costruire una singola API GraphQL unificata da più servizi GraphQL sottostanti (che spesso rappresentano microservizi). Consente agli sviluppatori di interrogare i dati tra diversi servizi come se fosse un singolo grafo, semplificando l'esperienza del client e riducendo la necessità di una complessa logica di orchestrazione sul lato client.
Esistono due approcci principali a GraphQL Federation:
- Schema Stitching: Questo comporta la combinazione di più schemi GraphQL in un singolo schema unificato a livello di gateway. È un approccio precedente e si basa su librerie per gestire la combinazione di schemi e la delega di query.
- Apollo Federation: Questo è un approccio più recente e robusto che utilizza un linguaggio di schema dichiarativo e un pianificatore di query dedicato per gestire il processo di federation. Offre funzionalità avanzate come estensioni di tipo, direttive chiave e tracciamento distribuito.
Questo articolo si concentra su Schema Stitching, esplorandone i concetti, i vantaggi, i limiti e l'implementazione pratica.
Comprendere Schema Stitching
Schema Stitching è il processo di fusione di più schemi GraphQL in un singolo schema coeso. Questo schema unificato funge da facciata, nascondendo la complessità dei servizi sottostanti al client. Quando un client effettua una richiesta allo schema stitched, il gateway instrada in modo intelligente la richiesta ai servizi sottostanti appropriati, recupera i dati e combina i risultati prima di restituirli al client.
Immagina che sia così: hai più ristoranti (servizi) ciascuno specializzato in cucine diverse. Schema Stitching è come un menu universale che combina tutti i piatti di ogni ristorante. Quando un cliente (client) ordina dal menu universale, l'ordine viene instradato in modo intelligente alle cucine dei ristoranti appropriati, il cibo viene preparato e quindi combinato in un'unica consegna per il cliente.
Concetti chiave in Schema Stitching
- Schemi remoti: Questi sono i singoli schemi GraphQL di ciascun servizio sottostante. Ogni servizio espone il proprio schema, che definisce i dati e le operazioni che fornisce.
- Gateway: Il gateway è il componente centrale responsabile dell'unione degli schemi remoti e dell'esposizione dello schema unificato al client. Riceve le richieste del client, le instrada ai servizi appropriati e combina i risultati.
- Unione di schemi: Questo è il processo di combinazione degli schemi remoti in un singolo schema. Questo spesso comporta la ridenominazione di tipi e campi per evitare conflitti e la definizione di relazioni tra tipi tra diversi schemi.
- Delega di query: Quando un client effettua una richiesta allo schema stitched, il gateway deve delegare la richiesta ai servizi sottostanti appropriati per recuperare i dati. Ciò comporta la traduzione della query del client in una query che può essere compresa dal servizio remoto.
- Aggregazione dei risultati: Dopo che il gateway ha recuperato i dati dai servizi sottostanti, deve combinare i risultati in una singola risposta che può essere restituita al client. Questo spesso comporta la trasformazione dei dati per corrispondere alla struttura dello schema stitched.
Vantaggi di Schema Stitching
Schema Stitching offre diversi vantaggi interessanti per le organizzazioni che adottano un'architettura a microservizi:
- API unificata: Fornisce una singola API coerente per i client, semplificando l'accesso ai dati e riducendo la necessità per i client di interagire direttamente con più servizi. Ciò si traduce in un'esperienza di sviluppo più pulita e intuitiva.
- Complessità del client ridotta: I client devono solo interagire con lo schema unificato, proteggendoli dalla complessità dell'architettura a microservizi sottostante. Ciò semplifica lo sviluppo lato client e riduce la quantità di codice richiesta sul client.
- Maggiore scalabilità: Consente di scalare i singoli servizi in modo indipendente in base alle loro esigenze specifiche. Ciò migliora la scalabilità e la resilienza complessive del sistema. Ad esempio, un servizio utente che sperimenta un carico elevato può essere scalato senza influire su altri servizi come il catalogo prodotti.
- Manutenibilità migliorata: Promuove la modularità e la separazione delle preoccupazioni, rendendo più facile mantenere e far evolvere i singoli servizi. È meno probabile che le modifiche a un servizio influiscano su altri servizi.
- Adozione graduale: Può essere implementato in modo incrementale, consentendo di migrare gradualmente da un'architettura monolitica a un'architettura a microservizi. Puoi iniziare unendo le API esistenti e quindi scomponendo gradualmente il monolite in servizi più piccoli.
Limiti di Schema Stitching
Sebbene Schema Stitching offra numerosi vantaggi, è importante essere consapevoli dei suoi limiti:
- Complessità: L'implementazione e la gestione dello schema stitching può essere complesso, specialmente in sistemi grandi e complessi. Un'attenta pianificazione e progettazione sono essenziali.
- Overhead delle prestazioni: Il gateway introduce un certo overhead delle prestazioni a causa del livello extra di indirezione e della necessità di delegare query e aggregare risultati. Un'attenta ottimizzazione è fondamentale per ridurre al minimo questo overhead.
- Conflitti di schema: Possono sorgere conflitti durante l'unione di schemi da servizi diversi, specialmente se utilizzano gli stessi nomi di tipo o nomi di campo. Ciò richiede un'attenta progettazione dello schema e potenzialmente la ridenominazione di tipi e campi.
- Funzionalità avanzate limitate: Rispetto ad Apollo Federation, Schema Stitching manca di alcune funzionalità avanzate come le estensioni di tipo e le direttive chiave, che possono rendere più difficile la gestione delle relazioni tra tipi tra diversi schemi.
- Maturità degli strumenti: Gli strumenti e l'ecosistema che circondano Schema Stitching non sono così maturi come quelli che circondano Apollo Federation. Ciò può rendere più difficile il debug e la risoluzione dei problemi.
Implementazione pratica di Schema Stitching
Analizziamo un esempio semplificato di come implementare Schema Stitching utilizzando Node.js e la libreria graphql-tools
(una scelta popolare per lo schema stitching). Questo esempio coinvolge due microservizi: un Servizio Utente e un Servizio Prodotto.
1. Definisci gli schemi remoti
Innanzitutto, definisci gli schemi GraphQL per ciascuno dei servizi remoti.
Servizio Utente (user-service.js
):
const { buildSchema } = require('graphql');
const userSchema = buildSchema(`
type User {
id: ID!
name: String
email: String
}
type Query {
user(id: ID!): User
}
`);
const users = [
{ id: '1', name: 'Alice Smith', email: 'alice@example.com' },
{ id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
];
const userRoot = {
user: (args) => users.find(user => user.id === args.id),
};
module.exports = {
schema: userSchema,
rootValue: userRoot,
};
Servizio Prodotto (product-service.js
):
const { buildSchema } = require('graphql');
const productSchema = buildSchema(`
type Product {
id: ID!
name: String
price: Float
userId: ID! # Chiave esterna al Servizio Utente
}
type Query {
product(id: ID!): Product
}
`);
const products = [
{ id: '101', name: 'Laptop', price: 1200, userId: '1' },
{ id: '102', name: 'Smartphone', price: 800, userId: '2' },
];
const productRoot = {
product: (args) => products.find(product => product.id === args.id),
};
module.exports = {
schema: productSchema,
rootValue: productRoot,
};
2. Crea il Servizio Gateway
Ora, crea il servizio gateway che unirà i due schemi.
Servizio Gateway (gateway.js
):
const { stitchSchemas } = require('@graphql-tools/stitch');
const { makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { graphqlHTTP } = require('express-graphql');
const express = require('express');
const { introspectSchema } = require('@graphql-tools/wrap');
const { printSchema } = require('graphql');
const fetch = require('node-fetch');
async function createRemoteSchema(uri) {
const fetcher = async (params) => {
const response = await fetch(uri, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
return response.json();
};
const schema = await introspectSchema(fetcher);
return makeRemoteExecutableSchema({
schema,
fetcher,
});
}
async function main() {
const userSchema = await createRemoteSchema('http://localhost:4001/graphql');
const productSchema = await createRemoteSchema('http://localhost:4002/graphql');
const stitchedSchema = stitchSchemas({
subschemas: [
{ schema: userSchema },
{ schema: productSchema },
],
typeDefs: `
extend type Product {
user: User
}
`,
resolvers: {
Product: {
user: {
selectionSet: `{ userId }`,
resolve(product, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: userSchema,
operation: 'query',
fieldName: 'user',
args: {
id: product.userId,
},
context,
info,
});
},
},
},
},
});
const app = express();
app.use('/graphql', graphqlHTTP({
schema: stitchedSchema,
graphiql: true,
}));
app.listen(4000, () => console.log('Gateway server running on http://localhost:4000/graphql'));
}
main().catch(console.error);
3. Esegui i servizi
Dovrai eseguire il Servizio Utente e il Servizio Prodotto su porte diverse. Ad esempio:
Servizio Utente (porta 4001):
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./user-service');
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: rootValue,
graphiql: true,
}));
app.listen(4001, () => console.log('User service running on http://localhost:4001/graphql'));
Servizio Prodotto (porta 4002):
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./product-service');
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: rootValue,
graphiql: true,
}));
app.listen(4002, () => console.log('Product service running on http://localhost:4002/graphql'));
4. Interroga lo schema stitched
Ora puoi interrogare lo schema stitched tramite il gateway (in esecuzione sulla porta 4000). Puoi eseguire una query come questa:
query {
product(id: "101") {
id
name
price
user {
id
name
email
}
}
}
Questa query recupera il prodotto con ID "101" e recupera anche l'utente associato dal Servizio Utente, dimostrando come Schema Stitching ti consente di interrogare i dati tra più servizi in un'unica richiesta.
Tecniche avanzate di Schema Stitching
Oltre all'esempio di base, ecco alcune tecniche avanzate che possono essere utilizzate per migliorare l'implementazione di Schema Stitching:
- Delega dello schema: Questo ti consente di delegare parti di una query a servizi diversi in base ai dati richiesti. Ad esempio, potresti delegare la risoluzione di un tipo `User` al Servizio Utente e la risoluzione di un tipo `Product` al Servizio Prodotto.
- Trasformazione dello schema: Questo comporta la modifica dello schema di un servizio remoto prima che venga unito nello schema unificato. Questo può essere utile per rinominare tipi e campi, aggiungere nuovi campi o rimuovere campi esistenti.
- Resolver personalizzati: Puoi definire resolver personalizzati nel gateway per gestire trasformazioni di dati complesse o per recuperare dati da più servizi e combinarli in un unico risultato.
- Condivisione del contesto: È spesso necessario condividere informazioni di contesto tra il gateway e i servizi remoti, come token di autenticazione o ID utente. Ciò può essere ottenuto passando le informazioni di contesto come parte del processo di delega della query.
- Gestione degli errori: Implementa una robusta gestione degli errori per gestire correttamente gli errori che si verificano nei servizi remoti. Ciò può comportare la registrazione degli errori, la restituzione di messaggi di errore facili da usare o il tentativo di nuove richieste non riuscite.
Scegliere tra Schema Stitching e Apollo Federation
Sebbene Schema Stitching sia un'opzione valida per GraphQL Federation, Apollo Federation è diventata la scelta più popolare grazie alle sue funzionalità avanzate e alla migliore esperienza per gli sviluppatori. Ecco un confronto tra i due approcci:
Funzionalità | Schema Stitching | Apollo Federation |
---|---|---|
Definizione dello schema | Utilizza il linguaggio dello schema GraphQL esistente | Utilizza un linguaggio di schema dichiarativo con direttive |
Pianificazione della query | Richiede la delega manuale della query | Pianificazione automatica della query da parte dell'Apollo Gateway |
Estensioni di tipo | Supporto limitato | Supporto integrato per le estensioni di tipo |
Direttive chiave | Non supportato | Utilizza la direttiva @key per identificare le entità |
Tracciamento distribuito | Richiede l'implementazione manuale | Supporto integrato per il tracciamento distribuito |
Strumenti ed ecosistema | Strumenti meno maturi | Strumenti più maturi e una grande community |
Complessità | Può essere complesso da gestire in sistemi di grandi dimensioni | Progettato per sistemi grandi e complessi |
Quando scegliere Schema Stitching:
- Hai servizi GraphQL esistenti e vuoi combinarli rapidamente.
- Hai bisogno di una soluzione di federation semplice e non hai bisogno di funzionalità avanzate.
- Hai risorse limitate e vuoi evitare l'overhead di configurazione di Apollo Federation.
Quando scegliere Apollo Federation:
- Stai costruendo un sistema grande e complesso con più team e servizi.
- Hai bisogno di funzionalità avanzate come estensioni di tipo, direttive chiave e tracciamento distribuito.
- Vuoi una soluzione di federation più robusta e scalabile.
- Preferisci un approccio più dichiarativo e automatizzato alla federation.
Esempi reali e casi d'uso
Ecco alcuni esempi reali di come GraphQL Federation, incluso Schema Stitching, può essere utilizzato:
- Piattaforma di e-commerce: Una piattaforma di e-commerce potrebbe utilizzare GraphQL Federation per combinare dati da più servizi, come un servizio di catalogo prodotti, un servizio utente, un servizio ordini e un servizio di pagamento. Ciò consente ai client di recuperare facilmente tutte le informazioni di cui hanno bisogno per visualizzare i dettagli del prodotto, i profili utente, la cronologia degli ordini e le informazioni di pagamento.
- Piattaforma di social media: Una piattaforma di social media potrebbe utilizzare GraphQL Federation per combinare dati da servizi che gestiscono profili utente, post, commenti e Mi piace. Ciò consente ai client di recuperare in modo efficiente tutte le informazioni necessarie per visualizzare il profilo di un utente, i suoi post e i commenti e i Mi piace associati a tali post.
- Applicazione di servizi finanziari: Un'applicazione di servizi finanziari potrebbe utilizzare GraphQL Federation per combinare dati da servizi che gestiscono account, transazioni e investimenti. Ciò consente ai client di recuperare facilmente tutte le informazioni di cui hanno bisogno per visualizzare i saldi dei conti, la cronologia delle transazioni e i portafogli di investimento.
- Sistema di gestione dei contenuti (CMS): Un CMS può sfruttare GraphQL Federation per integrare dati da varie fonti come articoli, immagini, video e contenuti generati dagli utenti. Ciò consente un'API unificata per recuperare tutti i contenuti relativi a un argomento o autore specifico.
- Applicazione sanitaria: Integra i dati dei pazienti da diversi sistemi come cartelle cliniche elettroniche (EHR), risultati di laboratorio e pianificazione degli appuntamenti. Questo offre ai medici un unico punto di accesso alle informazioni complete sui pazienti.
Best practice per Schema Stitching
Per garantire un'implementazione di Schema Stitching di successo, segui queste best practice:
- Pianifica attentamente il tuo schema: Prima di iniziare a unire gli schemi, pianifica attentamente la struttura dello schema unificato. Ciò include la definizione delle relazioni tra tipi tra diversi schemi, la ridenominazione di tipi e campi per evitare conflitti e la considerazione dei modelli generali di accesso ai dati.
- Utilizza convenzioni di denominazione coerenti: Adotta convenzioni di denominazione coerenti per tipi, campi e operazioni tra tutti i servizi. Ciò contribuirà a evitare conflitti e a rendere più facile la comprensione dello schema unificato.
- Documenta il tuo schema: Documenta accuratamente lo schema unificato, incluse le descrizioni di tipi, campi e operazioni. Ciò renderà più facile per gli sviluppatori comprendere e utilizzare lo schema.
- Monitora le prestazioni: Monitora le prestazioni del gateway e dei servizi remoti per identificare e risolvere eventuali colli di bottiglia delle prestazioni. Utilizza strumenti come il tracciamento distribuito per tracciare le richieste tra più servizi.
- Implementa la sicurezza: Implementa misure di sicurezza appropriate per proteggere il gateway e i servizi remoti da accessi non autorizzati. Ciò può comportare l'utilizzo di meccanismi di autenticazione e autorizzazione, nonché la convalida dell'input e la codifica dell'output.
- Versiona il tuo schema: Man mano che fai evolvere i tuoi schemi, versionarli in modo appropriato per garantire che i client possano continuare a utilizzare le versioni precedenti dello schema senza interruzioni. Ciò contribuirà a evitare modifiche di rilievo e a garantire la compatibilità con le versioni precedenti.
- Automatizza la distribuzione: Automatizza la distribuzione del gateway e dei servizi remoti per garantire che le modifiche possano essere distribuite rapidamente e in modo affidabile. Ciò contribuirà a ridurre il rischio di errori e a migliorare l'agilità complessiva del sistema.
Conclusione
GraphQL Federation con Schema Stitching offre un approccio potente per costruire API unificate da più servizi in un'architettura a microservizi. Comprendendo i suoi concetti fondamentali, i vantaggi, i limiti e le tecniche di implementazione, puoi sfruttare Schema Stitching per semplificare l'accesso ai dati, migliorare la scalabilità e migliorare la manutenibilità. Sebbene Apollo Federation sia emersa come una soluzione più avanzata, Schema Stitching rimane un'opzione valida per scenari più semplici o quando si integrano servizi GraphQL esistenti. Considera attentamente le tue esigenze e i tuoi requisiti specifici per scegliere l'approccio migliore per la tua organizzazione.