Estrategia de API Gateway y escalabilidad - MiguelAngelMarinMejia1/ArquitecturaDeSoftware-Proyecto GitHub Wiki

Qué es un API Gateway y por qué importa aquí

Un API Gateway es el único punto de entrada por el que pasan todas las peticiones antes de llegar a los servicios que las atienden. Concentra las preocupaciones transversales —enrutamiento, autenticación, límites de tasa, TLS, observabilidad— para que cada servicio se dedique a su negocio.

Hoy Hotel MDJ es un monolito Django, y eso es lo correcto para su tamaño. Lo que se decidió desde el principio es que nada en el diseño impida ponerlo detrás de un gateway el día que haga falta. Esta sección explica qué decisiones concretas garantizan eso.

                          ┌─────────────────────┐
   Navegador  ──────────► │                     │
                          │    API GATEWAY      │ ── enrutamiento por ruta
   App móvil  ──────────► │  (Kong, AWS API GW, │ ── TLS, rate limiting
                          │   Nginx, Traefik)   │ ── autenticación, métricas
   Canal OTA  ──────────► │                     │
                          └──────────┬──────────┘
                                     │
                ┌────────────────────┼────────────────────┐
                ▼                    ▼                    ▼
       /api/v1/reservations   /api/v1/rooms      /api/v2/... (futuro)
                │                    │
                ▼                    ▼
        ┌──────────────────────────────────────┐
        │   Servicio de reservas (este repo)   │
        │   núcleo: reservation/               │
        └──────────────────────────────────────┘

Decisiones que habilitan el gateway

1. Versionado en la ruta desde el primer día

Todos los endpoints cuelgan de /api/v1/. La configuración lo declara explícitamente:

# hotelMDJ/urls.py
urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", include("reservation.api.urls")),
    path("", include("web.urls")),
]

Un gateway enruta por prefijo de ruta sin inspeccionar el cuerpo de la petición. Cuando exista una v2 con un contrato incompatible, ambas convivirán y el gateway dirigirá cada una a su destino mientras los clientes migran a su ritmo. Versionar después de tener clientes en producción es una migración forzada; versionar desde el inicio no cuesta nada.

2. La API no guarda estado de sesión

Ningún endpoint de /api/v1/ depende de request.session ni de cookies. Cada petición lleva toda la información que necesita. Esto es lo que permite:

  • Escalado horizontal: cualquier réplica puede atender cualquier petición, sin sesiones pegajosas ni almacén de sesiones compartido.
  • Balanceo trivial: el gateway reparte round-robin sin restricciones.
  • Reinicios sin impacto: tumbar una instancia no desconecta a nadie.

El sitio web sí usa sesión, pero solo para un dato efímero: el token que autoriza ver la página de confirmación justo después de reservar. Esa dependencia vive en web/, nunca en la API.

3. Contrato de errores uniforme

Todos los errores salen con la misma forma, sean de dominio o de DRF:

{
  "error": {
    "code": "ROOMNOTAVAILABLE",
    "detail": "La habitacion 103 no esta disponible entre 2026-09-03 y 2026-09-06."
  }
}

Esto lo garantiza reservation/api/exception_handler.py, registrado globalmente. Un gateway puede aplicar políticas sobre el código HTTP y un cliente puede parsear errores sin conocer el endpoint.

Situación HTTP
Reserva creada 201 Created
Payload mal formado o regla de negocio violada 400 Bad Request
Pago rechazado 402 Payment Required
Habitación o reserva inexistente 404 Not Found
Habitación ocupada en esas fechas 409 Conflict

El 409 merece mención aparte: es exactamente el código que HTTP reserva para un conflicto con el estado actual del recurso. Devolver 400 ahí sería técnicamente incorrecto e impediría que un cliente distinga "me equivoqué al escribir" de "alguien reservó antes que yo".

4. El núcleo no conoce el transporte

reservation/domain/, reservation/services.py y reservation/selectors.py no importan nada de rest_framework ni de django.http. Solo reservation/api/ sabe que existe HTTP.

Esta es la propiedad que hace viable extraer el servicio: si mañana la gestión de reservas se separa en un microservicio, se lleva models.py, domain/, services.py y selectors.py sin modificar una línea, y solo se reescribe la capa de exposición.

5. Endpoint de salud

GET /api/v1/health/ responde sin tocar la base de datos. Es lo que un gateway, un balanceador o un orquestador consultan para decidir si una instancia recibe tráfico.

6. Separación entre lecturas y escrituras

Las consultas viven en selectors.py y los comandos en services.py. Cuando el volumen lo exija, las lecturas pueden dirigirse a réplicas de solo lectura sin tocar el código de escritura: el gateway puede incluso enrutar los GET a un pool distinto del de los POST.

Endpoints publicados

Método Ruta Propósito Códigos
GET /api/v1/health/ Sonda de vida 200
GET /api/v1/room-types/ Catálogo de tipos 200
GET /api/v1/rooms/available/ Disponibilidad por fechas 200, 400
POST /api/v1/reservations/ Crear reserva 201, 400, 402, 404, 409
POST /api/v1/reservations/lookup/ Consultar reserva 200, 400, 404

lookup es POST y no GET deliberadamente: necesita el correo del huésped, y un GET lo dejaría escrito en el historial del navegador y en los registros de acceso del gateway.

Qué falta para producción

Estas piezas no corresponden a esta entrega, pero el diseño ya las contempla:

  • Autenticación de la API: hoy los endpoints son abiertos. El gateway sería el lugar natural para validar claves de API o tokens antes de que la petición llegue al servicio.
  • Límites de tasa: POST /reservations/ es susceptible de abuso. DRF trae throttling y el gateway puede aplicarlo antes.
  • Caché: GET /room-types/ cambia rara vez y es candidato inmediato a caché en el gateway.
  • Idempotencia: una cabecera Idempotency-Key evitaría reservas duplicadas por reintentos de red. Hoy el sitio web lo mitiga con Post/Redirect/Get, pero la API no tiene esa protección.
  • Observabilidad: identificador de correlación por petición, propagado por el gateway.