Kurz gesagt (TL;DR)
Erstellen Sie Multi-Protokoll-APIs, indem Sie die Geschäftslogik von den Protokollschichten trennen. Erstellen Sie eine gemeinsame Domänenschicht und fügen Sie dann REST-, GraphQL- und gRPC-Adapter hinzu. Die moderne PetstoreAPI demonstriert diese Architektur mit konsistenten Datenmodellen über alle drei Protokolle hinweg.
Einführung
Ihre API bedient Web-Clients, mobile Anwendungen und interne Microservices. Web-Clients bevorzugen REST für Einfachheit. Mobile Anwendungen wünschen sich GraphQL zur Reduzierung des Datentransfers. Microservices benötigen gRPC für Performance. Bauen Sie drei separate APIs?
Nein. Sie bauen eine API mit drei Protokollschichten. Die Geschäftslogik bleibt gleich. Nur die Protokolladapter ändern sich. Dies ist die Multi-Protokoll-API-Architektur.
Die moderne PetstoreAPI implementiert REST, GraphQL und gRPC aus einem gemeinsamen Kern. Dieselbe Geschäftslogik des Tierladens bedient alle drei Protokolle mit konsistentem Verhalten.
In diesem Leitfaden erfahren Sie, wie Sie Multi-Protokoll-APIs architekturieren, Protokollschichten implementieren und die Konsistenz über Protokolle hinweg sicherstellen, basierend auf der Modernen PetstoreAPI als Referenz.
Multi-Protokoll-Architektur
Multi-Protokoll-APIs trennen Zuständigkeiten in Schichten.
Schichtenarchitektur
┌─────────────────────────────────────────┐
│ Protokollschicht (REST/GraphQL/gRPC) │
├─────────────────────────────────────────┤
│ Anwendungsschicht (Anwendungsfälle) │
├─────────────────────────────────────────┤
│ Domänenschicht (Geschäftslogik) │
├─────────────────────────────────────────┤
│ Datenschicht (Datenbank, Cache) │
└─────────────────────────────────────────┘
Protokollschicht: Verarbeitet HTTP, GraphQL-Abfragen, gRPC-Aufrufe Anwendungsschicht: Orchestriert Anwendungsfälle (Tier erstellen, Bestellung aufgeben) Domänenschicht: Geschäftsregeln (Validierung, Berechnungen) Datenschicht: Persistenz (Datenbank, Cache)
Schlüsselprinzipien
1. Protokoll-agnostischer Kern
Die Geschäftslogik kennt HTTP, GraphQL oder gRPC nicht. Sie arbeitet mit Domänenobjekten.
2. Schlanke Protokolladapter
Protokollschichten übersetzen zwischen Protokollformaten und Domänenobjekten. Sie enthalten keine Geschäftslogik.
3. Geteilte Datenmodelle
Alle Protokolle verwenden intern dieselben Domänenmodelle, um Konsistenz zu gewährleisten.
4. Unabhängige Bereitstellung
Jedes Protokoll kann bei Bedarf separat bereitgestellt werden.
Gemeinsame Domänenschicht
Die Domänenschicht enthält Geschäftslogik, die über alle Protokolle hinweg geteilt wird.
Domänenmodelle
// Domänenmodell (protokoll-agnostisch)
class Pet {
id: string;
name: string;
species: Species;
status: PetStatus;
price: number;
constructor(data: PetData) {
this.validate(data);
Object.assign(this, data);
}
validate(data: PetData): void {
if (!data.name || data.name.length < 2) {
throw new ValidationError('Name must be at least 2 characters');
}
if (data.price < 0) {
throw new ValidationError('Price cannot be negative');
}
}
adopt(userId: string): Order {
if (this.status !== PetStatus.AVAILABLE) {
throw new BusinessError('Pet is not available for adoption');
}
this.status = PetStatus.ADOPTED;
return new Order({
petId: this.id,
userId,
total: this.price
});
}
}
Dieses Modell funktioniert für REST, GraphQL und gRPC. Die Validierungs- und Geschäftsregeln sind dieselben.
Anwendungsfälle
// Anwendungsfall (protokoll-agnostisch)
class AdoptPetUseCase {
constructor(
private petRepository: PetRepository,
private orderRepository: OrderRepository
) {}
async execute(petId: string, userId: string): Promise<Order> {
const pet = await this.petRepository.findById(petId);
if (!pet) {
throw new NotFoundError('Pet not found');
}
const order = pet.adopt(userId);
await this.petRepository.save(pet);
await this.orderRepository.save(order);
return order;
}
}
Dieser Anwendungsfall funktioniert unabhängig davon, ob er von REST, GraphQL oder gRPC aufgerufen wird.
REST-Protokollschicht
Die REST-Schicht übersetzt HTTP-Anfragen in Domänenoperationen.
REST-Controller
// REST-Adapter
class PetsController {
constructor(private adoptPetUseCase: AdoptPetUseCase) {}
async adoptPet(req: Request, res: Response): Promise<void> {
try {
const { petId } = req.params;
const { userId } = req.body;
const order = await this.adoptPetUseCase.execute(petId, userId);
res.status(201).json({
id: order.id,
petId: order.petId,
userId: order.userId,
total: order.total,
status: order.status
});
} catch (error) {
this.handleError(error, res);
}
}
private handleError(error: Error, res: Response): void {
if (error instanceof NotFoundError) {
res.status(404).json({
type: 'https://petstoreapi.com/errors/not-found',
title: 'Nicht gefunden',
status: 404,
detail: error.message
});
} else if (error instanceof ValidationError) {
res.status(400).json({
type: 'https://petstoreapi.com/errors/validation-error',
title: 'Validierungsfehler',
status: 400,
detail: error.message
});
} else {
res.status(500).json({
type: 'https://petstoreapi.com/errors/internal-error',
title: 'Interner Serverfehler',
status: 500
});
}
}
}
REST-Routen
app.post('/v1/pets/:petId/adopt', (req, res) =>
petsController.adoptPet(req, res)
);
REST-Endpunkte der Modernen PetstoreAPI
GraphQL-Protokollschicht
Die GraphQL-Schicht übersetzt GraphQL-Abfragen in Domänenoperationen.
GraphQL-Schema
type Pet {
id: ID!
name: String!
species: Species!
status: PetStatus!
price: Float!
}
type Order {
id: ID!
petId: ID!
userId: ID!
total: Float!
status: OrderStatus!
}
type Mutation {
adoptPet(petId: ID!, userId: ID!): Order!
}
GraphQL-Resolver
// GraphQL-Adapter
const resolvers = {
Mutation: {
adoptPet: async (
_parent: any,
args: { petId: string; userId: string },
context: Context
): Promise<Order> => {
try {
return await context.adoptPetUseCase.execute(
args.petId,
args.userId
);
} catch (error) {
throw new GraphQLError(error.message, {
extensions: {
code: error instanceof NotFoundError ? 'NOT_FOUND' :
error instanceof ValidationError ? 'BAD_USER_INPUT' :
'INTERNAL_SERVER_ERROR'
}
});
}
}
}
};
GraphQL-Anfrage
mutation {
adoptPet(
petId: "019b4132-70aa-764f-b315-e2803d882a24"
userId: "user-123"
) {
id
total
status
}
}
GraphQL-Schema der Modernen PetstoreAPI
gRPC-Protokollschicht
Die gRPC-Schicht übersetzt Protocol-Buffer-Nachrichten in Domänenoperationen.
Protocol-Buffer-Definition
syntax = "proto3";
package petstore.v1;
service PetService {
rpc AdoptPet(AdoptPetRequest) returns (AdoptPetResponse);
}
message AdoptPetRequest {
string pet_id = 1;
string user_id = 2;
}
message AdoptPetResponse {
string order_id = 1;
string pet_id = 2;
string user_id = 3;
double total = 4;
string status = 5;
}
gRPC-Dienstimplementierung
// gRPC-Adapter
class PetServiceImpl implements IPetService {
constructor(private adoptPetUseCase: AdoptPetUseCase) {}
async adoptPet(
call: ServerUnaryCall<AdoptPetRequest, AdoptPetResponse>,
callback: sendUnaryData<AdoptPetResponse>
): Promise<void> {
try {
const { petId, userId } = call.request;
const order = await this.adoptPetUseCase.execute(petId, userId);
callback(null, {
orderId: order.id,
petId: order.petId,
userId: order.userId,
total: order.total,
status: order.status
});
} catch (error) {
callback({
code: error instanceof NotFoundError ? status.NOT_FOUND :
error instanceof ValidationError ? status.INVALID_ARGUMENT :
status.INTERNAL,
message: error.message
});
}
}
}
gRPC-Dienst der Modernen PetstoreAPI
Wie die moderne PetstoreAPI Multi-Protokoll implementiert
Die moderne PetstoreAPI demonstriert Multi-Protokoll-Architektur mit echten Beispielen.
Architekturübersicht
Moderne PetstoreAPI
├── Domänenschicht
│ ├── Pet (Entität)
│ ├── Order (Entität)
│ └── Anwendungsfälle
│ ├── CreatePet
│ ├── AdoptPet
│ └── PlaceOrder
├── REST-Schicht
│ ├── /v1/pets
│ ├── /v1/orders
│ └── OpenAPI 3.2 Spezifikation
├── GraphQL-Schicht
│ ├── Query-Resolver
│ ├── Mutation-Resolver
│ └── GraphQL-Schema
└── gRPC-Schicht
├── PetService
├── OrderService
└── .proto-Definitionen
Konsistente Datenmodelle
Alle Protokolle geben dieselben Daten zurück:
REST:
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
GraphQL:
{
"data": {
"pet": {
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
}
}
gRPC:
{
pet_id: "019b4132-70aa-764f-b315-e2803d882a24"
name: "Fluffy"
species: CAT
}
Dieselbe Daten, unterschiedliche Formate.
Gemeinsame Validierung
Die Validierung erfolgt in der Domänenschicht, daher setzen alle Protokolle dieselben Regeln durch:
// Domänenvalidierung (geteilt)
if (name.length < 2) {
throw new ValidationError('Name must be at least 2 characters');
}
REST gibt zurück:
{
"type": "https://petstoreapi.com/errors/validation-error",
"status": 400,
"detail": "Name must be at least 2 characters"
}
GraphQL gibt zurück:
{
"errors": [{
"message": "Name must be at least 2 characters",
"extensions": {"code": "BAD_USER_INPUT"}
}]
}
gRPC gibt zurück:
code: INVALID_ARGUMENT
message: "Name must be at least 2 characters"
Dieselbe Validierung, protokollspezifische Fehlerformate.
Testen von Multi-Protokoll-APIs mit Apidog
Apidog unterstützt das Testen aller drei Protokolle.
REST-Endpunkt testen
POST https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/adopt
Content-Type: application/json
{
"userId": "user-123"
}
GraphQL-Mutation testen
mutation {
adoptPet(
petId: "019b4132-70aa-764f-b315-e2803d882a24"
userId: "user-123"
) {
id
total
}
}
gRPC-Dienst testen
grpc.petstore.v1.PetService/AdoptPet
{
"pet_id": "019b4132-70aa-764f-b315-e2803d882a24",
"user_id": "user-123"
}
Konsistenz überprüfen
Testen Sie, ob alle drei Protokolle dieselben Daten zurückgeben:
- REST-Endpunkt aufrufen
- GraphQL-Mutation aufrufen
- gRPC-Dienst aufrufen
- Ergebnisse vergleichen
Apidog kann dieses protokollübergreifende Testen automatisieren.
Bereitstellungsstrategien
Strategie 1: Einzelner Dienst
Alle Protokolle in einem Dienst bereitstellen:
┌─────────────────────────┐
│ PetstoreAPI-Dienst │
│ ├── REST (Port 8080) │
│ ├── GraphQL (Port 8081)│
│ └── gRPC (Port 50051) │
└─────────────────────────┘
Vorteile: Einfache Bereitstellung, gemeinsame Ressourcen Nachteile: Alle Protokolle skalieren zusammen
Strategie 2: Separate Dienste
Jedes Protokoll separat bereitstellen:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ REST-Dienst │ │ GraphQL-Dienst│ │ gRPC-Dienst │
│ (Port 8080) │ │ (Port 8081) │ │ (Port 50051) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└─────────────────┴──────────────────┘
│
┌──────────────┐
│ Gemeinsame │
│ Core-Lib │
└──────────────┘
Vorteile: Unabhängige Skalierung, Protokollisolierung Nachteile: Komplexere Bereitstellung
Strategie 3: API-Gateway
Ein API-Gateway verwenden, um Anfragen an protokollspezifische Backends weiterzuleiten:
┌─────────────┐
│ API-Gateway │
└─────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ REST-Backend │ │ GraphQL │ │ gRPC-Backend│
└──────────────┘ └──────────┘ └─────────────┘
Vorteile: Zentrales Routing, Ratenbegrenzung, Authentifizierung Nachteile: Zusätzliche Latenz, Gateway-Komplexität
Die moderne PetstoreAPI verwendet Strategie 1 der Einfachheit halber.
Fazit
Multi-Protokoll-APIs bieten Clients Flexibilität, ohne Geschäftslogik zu duplizieren. Durch die Trennung von Protokollschichten und Domänenlogik können Sie REST, GraphQL und gRPC von einem gemeinsamen Kern aus unterstützen.
Die moderne PetstoreAPI demonstriert diese Architektur mit konsistenten Datenmodellen, gemeinsamer Validierung und protokollspezifischen Adaptern. Ob Clients REST für Einfachheit, GraphQL für Flexibilität oder gRPC für Performance verwenden, sie greifen auf denselben Tierladen mit denselben Geschäftsregeln zu.
Testen Sie Ihre Multi-Protokoll-APIs mit Apidog, um die Konsistenz über Protokolle hinweg sicherzustellen. Entdecken Sie die Moderne PetstoreAPI, um die Multi-Protokoll-Architektur in Aktion zu sehen.
FAQ
Muss ich alle drei Protokolle unterstützen?
Nein. Beginnen Sie mit REST für öffentliche APIs. Fügen Sie GraphQL hinzu, wenn Clients eine flexible Datenabfrage benötigen. Fügen Sie gRPC für interne Microservices hinzu. Fügen Sie Protokolle nur hinzu, wenn Sie einen klaren Anwendungsfall haben.
Wie halte ich Protokolle konsistent?
Teilen Sie die Geschäftslogik in einer Domänenschicht. Protokolladapter sollten nur Formate übersetzen und keine Geschäftsregeln enthalten. Testen Sie alle Protokolle, um zu überprüfen, ob sie dieselben Daten zurückgeben.
Kann ich Protokolle unabhängig voneinander versionieren?
Ja. REST kann in v2 sein, während GraphQL in v1 ist. Dies führt jedoch zu Komplexität. Versuchen Sie, Protokolle nach Möglichkeit synchron zu halten.
Wie gehe ich mit der Authentifizierung über Protokolle hinweg um?
Verwenden Sie dieselbe Authentifizierung in allen Protokollen. REST verwendet Bearer-Tokens in Headern. GraphQL verwendet dieselben Tokens. gRPC verwendet Metadaten. Die Domänenschicht validiert Tokens auf dieselbe Weise.
Was ist mit WebSocket und SSE?
WebSocket und SSE sind Transportprotokolle, keine API-Protokolle. Sie können sie neben REST/GraphQL/gRPC für Echtzeit-Updates hinzufügen. Die moderne PetstoreAPI enthält beides.
Wie dokumentiere ich Multi-Protokoll-APIs?
Verwenden Sie OpenAPI für REST, GraphQL-Schema für GraphQL und .proto-Dateien für gRPC. Die moderne PetstoreAPI bietet alle drei unter https://docs.petstoreapi.com/ an.
Kann ich unterschiedliche Datenbanken für unterschiedliche Protokolle verwenden?
Ja, aber das führt zu Konsistenzproblemen. Es ist besser, dieselbe Datenschicht für alle Protokolle zu verwenden. Lassen Sie die Protokolladapter die Formatunterschiede handhaben.
Wie teste ich Multi-Protokoll-APIs?
Verwenden Sie Apidog, um alle Protokolle in einem Tool zu testen. Erstellen Sie Test-Suites, die die Konsistenz über Protokolle hinweg überprüfen. Testen Sie, ob REST, GraphQL und gRPC für dieselben Operationen dieselben Daten zurückgeben.
