Saltar a contenido

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

  1. Client-Server: Separación de responsabilidades
  2. Stateless: Cada request contiene toda la información necesaria
  3. Cacheable: Las respuestas deben indicar si son cacheables
  4. Uniform Interface: Recursos identificados por URIs, manipulados a través de representaciones
  5. Layered System: El cliente no sabe si habla con el servidor final o un intermediario
  6. 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)