Em resumo
Construa APIs multi-protocolo separando a lógica de negócios das camadas de protocolo. Crie uma camada de domínio compartilhada e adicione adaptadores REST, GraphQL e gRPC por cima. A Modern PetstoreAPI demonstra esta arquitetura com modelos de dados consistentes em todos os três protocolos.
Introdução
Sua API atende a clientes web, aplicativos móveis e microsserviços internos. Clientes web querem REST para simplicidade. Aplicativos móveis querem GraphQL para reduzir a transferência de dados. Microsserviços querem gRPC para desempenho. Você constrói três APIs separadas?
Não. Você constrói uma API com três camadas de protocolo. A lógica de negócios permanece a mesma. Apenas os adaptadores de protocolo mudam. Esta é a arquitetura de API multi-protocolo.
A Modern PetstoreAPI implementa REST, GraphQL e gRPC a partir de um núcleo compartilhado. A mesma lógica da pet shop atende aos três protocolos com comportamento consistente.
Neste guia, você aprenderá como arquitetar APIs multi-protocolo, implementar camadas de protocolo e garantir a consistência entre protocolos usando a Modern PetstoreAPI como referência.
Arquitetura Multi-Protocolo
APIs multi-protocolo separam as preocupações em camadas.
Arquitetura em Camadas
┌─────────────────────────────────────────┐
│ Camada de Protocolo (REST/GraphQL/gRPC)│
├─────────────────────────────────────────┤
│ Camada de Aplicação (Casos de Uso) │
├─────────────────────────────────────────┤
│ Camada de Domínio (Lógica de Negócios) │
├─────────────────────────────────────────┤
│ Camada de Dados (Banco de Dados, Cache)│
└─────────────────────────────────────────┘
Camada de Protocolo: Lida com HTTP, consultas GraphQL, chamadas gRPC Camada de Aplicação: Orquestra casos de uso (criar pet, fazer pedido) Camada de Domínio: Regras de negócios (validação, cálculos) Camada de Dados: Persistência (banco de dados, cache)
Princípios Fundamentais
1. Núcleo agnóstico ao protocolo
A lógica de negócios não conhece HTTP, GraphQL ou gRPC. Ela trabalha com objetos de domínio.
2. Adaptadores de protocolo finos
As camadas de protocolo traduzem entre formatos de protocolo e objetos de domínio. Elas não contêm lógica de negócios.
3. Modelos de dados compartilhados
Todos os protocolos usam os mesmos modelos de domínio internamente, garantindo consistência.
4. Implantação independente
Cada protocolo pode ser implantado separadamente, se necessário.
Camada de Domínio Compartilhada
A camada de domínio contém a lógica de negócios compartilhada entre todos os protocolos.
Modelos de Domínio
// Modelo de domínio (agnóstico ao protocolo)
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
});
}
}
Este modelo funciona para REST, GraphQL e gRPC. A validação e as regras de negócios são as mesmas.
Casos de Uso
// Caso de uso (agnóstico ao protocolo)
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;
}
}
Este caso de uso funciona independentemente de ser chamado por REST, GraphQL ou gRPC.
Camada de Protocolo REST
A camada REST traduz requisições HTTP para operações de domínio.
Controller REST
// Adaptador REST
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: 'Not Found',
status: 404,
detail: error.message
});
} else if (error instanceof ValidationError) {
res.status(400).json({
type: 'https://petstoreapi.com/errors/validation-error',
title: 'Validation Error',
status: 400,
detail: error.message
});
} else {
res.status(500).json({
type: 'https://petstoreapi.com/errors/internal-error',
title: 'Internal Server Error',
status: 500
});
}
}
}
Rotas REST
app.post('/v1/pets/:petId/adopt', (req, res) =>
petsController.adoptPet(req, res)
);
Endpoints REST da Modern PetstoreAPI
Camada de Protocolo GraphQL
A camada GraphQL traduz consultas GraphQL para operações de domínio.
Esquema GraphQL
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!
}
Resolver GraphQL
// Adaptador GraphQL
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'
}
});
}
}
}
};
Requisição GraphQL
mutation {
adoptPet(
petId: "019b4132-70aa-764f-b315-e2803d882a24"
userId: "user-123"
) {
id
total
status
}
}
Esquema GraphQL da Modern PetstoreAPI
Camada de Protocolo gRPC
A camada gRPC traduz mensagens do Protocol Buffer para operações de domínio.
Definição do Protocol Buffer
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;
}
Implementação do Serviço gRPC
// Adaptador gRPC
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
});
}
}
}
Serviço gRPC da Modern PetstoreAPI
Como a Modern PetstoreAPI Implementa Multi-Protocolo
A Modern PetstoreAPI demonstra a arquitetura multi-protocolo com exemplos reais.
Visão Geral da Arquitetura
Modern PetstoreAPI
├── Camada de Domínio
│ ├── Pet (entidade)
│ ├── Order (entidade)
│ └── Casos de Uso
│ ├── CreatePet
│ ├── AdoptPet
│ └── PlaceOrder
├── Camada REST
│ ├── /v1/pets
│ ├── /v1/orders
│ └── especificação OpenAPI 3.2
├── Camada GraphQL
│ ├── Resolvers de consulta
│ ├── Resolvers de mutação
│ └── Esquema GraphQL
└── Camada gRPC
├── PetService
├── OrderService
└── Definições .proto
Modelos de Dados Consistentes
Todos os protocolos retornam os mesmos dados:
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
}
Mesmos dados, formatos diferentes.
Validação Compartilhada
A validação ocorre na camada de domínio, então todos os protocolos impõem as mesmas regras:
// Validação de domínio (compartilhada)
if (name.length < 2) {
throw new ValidationError('Name must be at least 2 characters');
}
REST retorna:
{
"type": "https://petstoreapi.com/errors/validation-error",
"status": 400,
"detail": "Name must be at least 2 characters"
}
GraphQL retorna:
{
"errors": [{
"message": "Name must be at least 2 characters",
"extensions": {"code": "BAD_USER_INPUT"}
}]
}
gRPC retorna:
code: INVALID_ARGUMENT
message: "Name must be at least 2 characters"
Mesma validação, formatos de erro específicos do protocolo.
Testando APIs Multi-Protocolo com Apidog
Apidog suporta o teste dos três protocolos.
Testar Endpoint REST
POST https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/adopt
Content-Type: application/json
{
"userId": "user-123"
}
Testar Mutação GraphQL
mutation {
adoptPet(
petId: "019b4132-70aa-764f-b315-e2803d882a24"
userId: "user-123"
) {
id
total
}
}
Testar Serviço gRPC
grpc.petstore.v1.PetService/AdoptPet
{
"pet_id": "019b4132-70aa-764f-b315-e2803d882a24",
"user_id": "user-123"
}
Verificar Consistência
Teste se todos os três protocolos retornam os mesmos dados:
- Chamar o endpoint REST
- Chamar a mutação GraphQL
- Chamar o serviço gRPC
- Comparar os resultados
O Apidog pode automatizar este teste entre protocolos.
Estratégias de Implantação
Estratégia 1: Serviço Único
Implante todos os protocolos em um único serviço:
┌─────────────────────────┐
│ Serviço PetstoreAPI │
│ ├── REST (porta 8080) │
│ ├── GraphQL (porta 8081)│
│ └── gRPC (porta 50051) │
└─────────────────────────┘
Prós: Implantação simples, recursos compartilhados Contras: Todos os protocolos escalam juntos
Estratégia 2: Serviços Separados
Implante cada protocolo separadamente:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Serviço REST │ │ Serviço GraphQL│ │ Serviço gRPC │
│ (porta 8080) │ │ (porta 8081) │ │ (porta 50051)│
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└─────────────────┴──────────────────┘
│
┌──────────────┐
│ Núcleo Compartilhado│
│ Biblioteca │
└──────────────┘
Prós: Escalonamento independente, isolamento de protocolo Contras: Implantação mais complexa
Estratégia 3: Gateway de API
Use um gateway de API para rotear para backends específicos do protocolo:
┌─────────────┐
│ API Gateway │
└─────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Backend REST │ │ GraphQL │ │ Backend gRPC│
└──────────────┘ └──────────┘ └─────────────┘
Prós: Roteamento centralizado, limitação de taxa, autenticação Contras: Latência adicional, complexidade do gateway
A Modern PetstoreAPI usa a Estratégia 1 para simplicidade.
Conclusão
APIs multi-protocolo oferecem flexibilidade aos clientes sem duplicar a lógica de negócios. Ao separar as camadas de protocolo da lógica de domínio, você pode suportar REST, GraphQL e gRPC a partir de um núcleo compartilhado.
A Modern PetstoreAPI demonstra esta arquitetura com modelos de dados consistentes, validação compartilhada e adaptadores específicos do protocolo. Quer os clientes usem REST para simplicidade, GraphQL para flexibilidade ou gRPC para desempenho, eles acessam a mesma pet shop com as mesmas regras de negócio.
Teste suas APIs multi-protocolo com Apidog para garantir a consistência entre os protocolos. Explore a Modern PetstoreAPI para ver a arquitetura multi-protocolo em ação.
Perguntas Frequentes
Preciso dar suporte aos três protocolos?
Não. Comece com REST para APIs públicas. Adicione GraphQL se os clientes precisarem de busca de dados flexível. Adicione gRPC para microsserviços internos. Adicione protocolos apenas quando tiver um caso de uso claro.
Como mantenho os protocolos consistentes?
Compartilhe a lógica de negócios em uma camada de domínio. Os adaptadores de protocolo devem apenas traduzir formatos, não conter regras de negócios. Teste todos os protocolos para verificar se retornam os mesmos dados.
Posso versionar os protocolos de forma independente?
Sim. REST pode estar na v2 enquanto GraphQL está na v1. Mas isso cria complexidade. Tente manter os protocolos sincronizados sempre que possível.
Como faço a autenticação em todos os protocolos?
Use a mesma autenticação em todos os protocolos. REST usa tokens Bearer em cabeçalhos. GraphQL usa os mesmos tokens. gRPC usa metadados. A camada de domínio valida os tokens da mesma forma.
E quanto a WebSocket e SSE?
WebSocket e SSE são protocolos de transporte, não protocolos de API. Você pode adicioná-los junto com REST/GraphQL/gRPC para atualizações em tempo real. A Modern PetstoreAPI inclui ambos.
Como documento APIs multi-protocolo?
Use OpenAPI para REST, esquema GraphQL para GraphQL e arquivos .proto para gRPC. A Modern PetstoreAPI fornece todos os três em https://docs.petstoreapi.com/
Posso usar bancos de dados diferentes para protocolos diferentes?
Sim, mas isso cria desafios de consistência. É melhor usar a mesma camada de dados para todos os protocolos. Deixe que os adaptadores de protocolo lidem com as diferenças de formato.
Como testo APIs multi-protocolo?
Use o Apidog para testar todos os protocolos em uma única ferramenta. Crie conjuntos de testes que verifiquem a consistência entre os protocolos. Teste se REST, GraphQL e gRPC retornam os mesmos dados para as mesmas operações.
