04 - Patrones de Comunicación¶
Tabla de Contenidos¶
REST¶
REpresentational State Transfer. Estilo arquitectónico basado en recursos y verbos HTTP, propuesto por Roy Fielding en su tesis doctoral (2000).
Principios¶
- Client-Server: Separación de responsabilidades
- Stateless: Cada request contiene toda la información necesaria
- Cacheable: Las respuestas deben indicar si son cacheables
- Uniform Interface: Recursos identificados por URIs, manipulados a través de representaciones
- Layered System: El cliente no sabe si habla con el servidor final o un intermediario
- Code on Demand (opcional): El servidor puede enviar código ejecutable al cliente
Diseño de Recursos¶
GET /api/v1/users → Listar usuarios
GET /api/v1/users/123 → Obtener usuario 123
POST /api/v1/users → Crear usuario
PUT /api/v1/users/123 → Reemplazar usuario 123
PATCH /api/v1/users/123 → Actualizar parcialmente
DELETE /api/v1/users/123 → Eliminar usuario 123
# Recursos anidados
GET /api/v1/users/123/orders → Pedidos del usuario 123
GET /api/v1/users/123/orders/456 → Pedido 456 del usuario 123
Versionado de APIs¶
| Estrategia | Ejemplo | Pros/Cons |
|---|---|---|
| URL Path | /api/v2/users |
Simple, explícito. Rompe URIs. |
| Query Param | /api/users?version=2 |
Fácil de ignorar. Menos limpio. |
| Header | Accept: application/vnd.api.v2+json |
No contamina URL. Menos visible. |
| Content Negotiation | Accept: application/json; version=2 |
Estándar HTTP. Complejo de implementar. |
Paginación¶
// Offset-based (simple, problemas con datos en movimiento)
GET /api/users?page=2&limit=20
// Cursor-based (consistente, ideal para feeds)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
// Response
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTQzfQ",
"has_more": true
}
}
Problemas Comunes de REST¶
- Over-fetching: Recibes más datos de los que necesitas
- Under-fetching: Necesitas múltiples requests para obtener datos relacionados (N+1 problem)
- Versionado: Mantener múltiples versiones en paralelo
GraphQL¶
Lenguaje de consulta para APIs desarrollado por Facebook (2015). El cliente define exactamente qué datos necesita.
Arquitectura¶
graph LR
C1[Mobile Client] --> GQL[GraphQL Server<br/>Single Endpoint<br/>POST /graphql]
C2[Web Client] --> GQL
C3[IoT Client] --> GQL
GQL --> S1[Users Service]
GQL --> S2[Orders Service]
GQL --> S3[Products Service]
Schema y Tipos¶
type User {
id: ID!
name: String!
email: String!
orders: [Order!]!
}
type Order {
id: ID!
total: Float!
items: [OrderItem!]!
createdAt: DateTime!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
type Subscription {
orderCreated(userId: ID!): Order!
}
Query del Cliente¶
# El cliente pide EXACTAMENTE lo que necesita
query {
user(id: "123") {
name
email
orders(last: 5) {
total
createdAt
}
}
}
Una sola request obtiene usuario + últimos 5 pedidos. Con REST serían 2+ requests.
Problemas de GraphQL¶
| Problema | Descripción | Mitigación |
|---|---|---|
| N+1 queries | Resolver orders por cada user genera N queries a DB |
DataLoader (batching + caching) |
| Query complexity | Queries profundamente anidadas pueden saturar el server | Depth limiting, cost analysis |
| Caching | Un solo endpoint POST dificulta HTTP caching | Persisted queries, CDN con Apollo |
| File uploads | GraphQL es JSON-only | Multipart spec o URLs pre-firmadas |
| Over-engineering | Para APIs simples, REST es más directo | Evaluar si la flexibilidad se necesita |
gRPC¶
Framework de RPC (Remote Procedure Call) de alto rendimiento creado por Google. Usa HTTP/2 y Protocol Buffers.
Arquitectura¶
graph LR
subgraph "Cliente"
CS[Client Stub<br/>Auto-generado]
end
subgraph "Red"
H2[HTTP/2<br/>Protocol Buffers<br/>Binary]
end
subgraph "Servidor"
SS[Server Stub<br/>Auto-generado]
IMPL[Implementación]
end
CS <-->|Serialización binaria| H2 <--> SS --> IMPL
Definición del Servicio (.proto)¶
syntax = "proto3";
service OrderService {
// Unary: request-response clásico
rpc GetOrder(GetOrderRequest) returns (Order);
// Server streaming: el server envía stream de respuestas
rpc ListOrders(ListOrdersRequest) returns (stream Order);
// Client streaming: el cliente envía stream de datos
rpc UploadOrders(stream Order) returns (UploadSummary);
// Bidirectional streaming: ambos envían streams
rpc OrderChat(stream OrderMessage) returns (stream OrderMessage);
}
message Order {
string id = 1;
string user_id = 2;
double total = 3;
repeated OrderItem items = 4;
OrderStatus status = 5;
}
enum OrderStatus {
PENDING = 0;
CONFIRMED = 1;
SHIPPED = 2;
DELIVERED = 3;
}
Patrones de Comunicación gRPC¶
graph TD
subgraph "Unary"
C1[Client] -->|1 Request| S1[Server]
S1 -->|1 Response| C1
end
subgraph "Server Streaming"
C2[Client] -->|1 Request| S2[Server]
S2 -->|Stream de N Responses| C2
end
subgraph "Client Streaming"
C3[Client] -->|Stream de N Requests| S3[Server]
S3 -->|1 Response| C3
end
subgraph "Bidirectional"
C4[Client] <-->|Streams en ambas<br/>direcciones| S4[Server]
end
Ventajas de gRPC¶
- Rendimiento: Protocol Buffers es 5-10x más rápido que JSON en serialización
- Tipado fuerte: Contratos definidos en
.proto, code generation automático - HTTP/2: Multiplexing, header compression, streaming nativo
- Deadlines/Timeouts: Propagación automática de timeouts entre servicios
- Interceptors: Middleware para auth, logging, retry
Cuándo NO Usar gRPC¶
- APIs públicas para consumidores web (browsers no soportan gRPC nativo → necesitas gRPC-Web)
- Cuando necesitas debugging fácil (binario no es human-readable)
- Equipo sin experiencia con Protobuf
Sistemas Basados en Eventos¶
Message Queue vs Event Streaming¶
graph TD
subgraph "Message Queue (Point-to-Point)"
P1[Producer] --> Q[Queue<br/>RabbitMQ / SQS]
Q --> C1[Consumer 1]
Q --> C2[Consumer 2]
Note1[Cada mensaje se consume<br/>UNA sola vez]
end
subgraph "Event Streaming (Pub/Sub + Log)"
P2[Producer] --> T[Topic/Partition<br/>Kafka / Kinesis]
T --> CG1[Consumer Group A<br/>Todos los mensajes]
T --> CG2[Consumer Group B<br/>Todos los mensajes]
Note2[Cada grupo consume<br/>TODOS los mensajes]
end
RabbitMQ¶
Broker de mensajes tradicional basado en el protocolo AMQP.
Conceptos clave:
graph LR
P[Producer] --> E[Exchange]
E -->|Routing Key: order.created| Q1[Queue: notifications]
E -->|Routing Key: order.created| Q2[Queue: analytics]
E -->|Routing Key: order.shipped| Q3[Queue: shipping]
Q1 --> C1[Notification Service]
Q2 --> C2[Analytics Service]
Q3 --> C3[Shipping Service]
Tipos de Exchange:
| Exchange | Routing | Caso de uso |
|----------|---------|-------------|
| Direct | Exact match de routing key | Una cola específica |
| Fanout | Broadcast a todas las colas | Notificar a todos los consumers |
| Topic | Pattern matching (order.*, #.shipped) | Routing flexible por patrones |
| Headers | Match por headers del mensaje | Routing complejo sin routing key |
Garantías de entrega: - At-most-once: Puede perder mensajes (sin ACK, sin persistencia) - At-least-once: Puede duplicar (ACK manual + persistencia). Lo más común. - Exactly-once: Teórico. En la práctica se logra con idempotencia en el consumer.
Apache Kafka¶
Plataforma de event streaming distribuida. No es simplemente un message broker.
Arquitectura:
graph TD
subgraph "Kafka Cluster"
subgraph "Topic: orders (3 partitions)"
P0[Partition 0<br/>offset: 0,1,2...N]
P1[Partition 1<br/>offset: 0,1,2...M]
P2[Partition 2<br/>offset: 0,1,2...K]
end
end
Producer1[Producer] -->|key: user_123| P0
Producer2[Producer] -->|key: user_456| P1
subgraph "Consumer Group A"
CA1[Consumer A1] --> P0
CA1 --> P1
CA2[Consumer A2] --> P2
end
subgraph "Consumer Group B"
CB1[Consumer B1] --> P0
CB1 --> P1
CB1 --> P2
end
Conceptos fundamentales:
| Concepto | Descripción |
|---|---|
| Topic | Categoría/feed de mensajes (como una tabla) |
| Partition | Subdivisión ordenada de un topic. Unidad de paralelismo |
| Offset | Posición secuencial de un mensaje dentro de una partition |
| Consumer Group | Grupo de consumers que se reparten las partitions |
| Retention | Kafka retiene mensajes por tiempo o tamaño (no los borra al consumir) |
Kafka vs RabbitMQ:
| Criterio | Kafka | RabbitMQ |
|---|---|---|
| Modelo | Event log (append-only) | Message queue (consume & delete) |
| Retención | Configurable (días/semanas) | Hasta que se consume |
| Throughput | Millones msg/seg | Miles msg/seg |
| Orden | Garantizado por partition | Garantizado por queue |
| Replay | Sí (re-leer desde offset) | No (una vez consumido, se va) |
| Routing | Básico (por topic/partition key) | Avanzado (exchanges, routing keys) |
| Caso de uso | Event sourcing, streaming, logs | Task queues, RPC, routing complejo |
Patrones de Mensajería¶
Saga Pattern¶
Para transacciones distribuidas entre microservicios:
sequenceDiagram
participant Order
participant Payment
participant Inventory
participant Shipping
Order->>Payment: Cobrar $100
Payment-->>Order: Cobro exitoso
Order->>Inventory: Reservar items
Inventory-->>Order: Reserva exitosa
Order->>Shipping: Crear envío
Shipping-->>Order: Error: zona no cubierta
Note over Order: Compensación (rollback)
Order->>Inventory: Liberar reserva
Order->>Payment: Reembolsar $100
Event Sourcing¶
En vez de guardar el estado actual, guardas todos los eventos que lo produjeron:
Event 1: CartCreated { userId: 123 }
Event 2: ItemAdded { productId: "ABC", qty: 2 }
Event 3: ItemAdded { productId: "XYZ", qty: 1 }
Event 4: ItemRemoved { productId: "ABC", qty: 1 }
Event 5: OrderPlaced { total: 45.00 }
Estado actual = replay de todos los eventos
Comparativa General¶
| Criterio | REST | GraphQL | gRPC | Message Queue |
|---|---|---|---|---|
| Protocolo | HTTP/1.1+ | HTTP/1.1+ | HTTP/2 | AMQP/TCP |
| Formato | JSON/XML | JSON | Protobuf (binario) | Cualquiera |
| Comunicación | Request-Response | Request-Response | Req-Resp + Streaming | Async (fire-and-forget) |
| Contrato | OpenAPI/Swagger | Schema SDL | .proto files | Schema Registry (Kafka) |
| Acoplamiento | Bajo | Bajo | Alto (shared proto) | Muy bajo |
| Rendimiento | Medio | Medio | Alto | Alto |
| Browser support | Nativo | Nativo | Necesita gRPC-Web | N/A (backend) |
| Caso de uso | APIs públicas, CRUD | Mobile, dashboards | Microservicios internos | Workflows async, desacoplamiento |
Diagrama de Decisión¶
graph TD
Start[¿Qué tipo de<br/>comunicación necesitas?] --> Q1{¿Síncrona o<br/>asíncrona?}
Q1 -->|Asíncrona| Q5{¿Necesitas replay<br/>de eventos?}
Q5 -->|Sí| KAFKA[Kafka / Event Streaming]
Q5 -->|No| Q6{¿Routing complejo?}
Q6 -->|Sí| RABBIT[RabbitMQ / AMQP]
Q6 -->|No| SQS[SQS / Simple Queue]
Q1 -->|Síncrona| Q2{¿API pública o<br/>interna?}
Q2 -->|Pública| Q3{¿Clientes con necesidades<br/>de datos muy diferentes?}
Q3 -->|Sí| GQL[GraphQL]
Q3 -->|No| REST[REST]
Q2 -->|Interna| Q4{¿Alto rendimiento<br/>crítico?}
Q4 -->|Sí| GRPC[gRPC]
Q4 -->|No| REST
Recursos Recomendados¶
- Libro: Designing Data-Intensive Applications - Martin Kleppmann (Capítulos 4, 11, 12)
- Libro: Enterprise Integration Patterns - Hohpe & Woolf
- Especificación: GraphQL Spec (spec.graphql.org)
- Documentación: gRPC Official Docs (grpc.io)
- Curso: Confluent Kafka 101 (gratuito, developer.confluent.io)
- Blog: Martin Fowler - "Event Sourcing", "CQRS", "Saga Pattern"
- Video: Hussein Nasser - "RabbitMQ vs Kafka" (YouTube)