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-Keyevitarí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.