On-premise, cloud o serverless: qué problema resuelve cada opción

arquitecturacloudserverlessawsjava

Hay una conversación que se repite cada vez que un equipo arranca un proyecto o hereda uno: «¿lo subimos a la nube o lo dejamos aquí?». Casi siempre llega con otra pegada: «¿y si lo hacemos serverless?».

La pregunta parece una sola. No lo es. Mezcla tres decisiones distintas, y por eso la discusión se estanca: cada persona en la sala está respondiendo una diferente.

En este artículo separo esas tres decisiones y después comparo los tres escenarios con la misma aplicación, para que lo que cambie sea la infraestructura y no el negocio.

Primero: son tres decisiones, no una

DecisiónPregunta que responde
Ubicación y provisión¿Dónde corre y cómo obtengo los recursos?
Ejecución y operación¿Quién mantiene servidores, sistema operativo y capacidad?
Diseño del software¿Cómo separo dominios, transacciones y responsabilidades?

On-premise dice dónde están los recursos: en el entorno de la empresa. Cloud describe un modelo para consumirlos bajo demanda, y una nube privada puede existir dentro o fuera de las instalaciones (NIST). Cuando aquí digo cloud, comparo sobre todo la nube pública con el centro de datos de la empresa.

Serverless es una forma de usar cloud, no un lugar. Sigue habiendo servidores: los gestiona el proveedor. Tampoco significa solo funciones: Fargate ejecuta contenedores sin que administres los hosts (AWS).

Y la tercera decisión, monolito modular, microservicios o eventos, es compatible con cualquiera de las dos primeras.

Lo que debes llevarte: un monolito Java puede correr en cloud, y varios microservicios pueden operar on-premise. Cambiar de despliegue no te obliga a dividir el dominio.

El caso que uso para comparar

Una tienda con frontend React, backend Java y procesamiento de pedidos. El recorrido es el mismo en los tres escenarios: el cliente inicia sesión, crea un pedido, recibe una respuesta y espera una confirmación asíncrona.

Infografía: la misma aplicación desplegada on-premise, en cloud con EC2 y en cloud serverless, con sus componentes de identidad, entrada, cómputo, datos y mensajería

No veo esto como una escalera que obliga a subir de on-premise a contenedores y luego a funciones. Los servicios gestionados amplían las opciones; cada carga puede pedir una combinación distinta.

On-premise: control local y operación propia

Este escenario existe para una necesidad concreta: ejecutar cerca de recursos que tienen que quedarse en la empresa. Una planta, una red de baja latencia, hardware especializado, integraciones locales o restricciones contractuales que ya verificaste. Tener infraestructura amortizada y un equipo capaz de operarla también suma.

El recorrido: React redirige el login a Keycloak y luego envía el access token al proxy de entrada (NGINX con WAF), que lo enruta a Spring Boot. La API valida identidad y permisos, calcula el pedido y escribe pedido y evento en una sola transacción de PostgreSQL. Otro proceso publica ese evento en RabbitMQ y un worker Java procesa la confirmación. La base de datos nunca queda expuesta al navegador.

Lo que ganas es control sobre red, hardware, ventanas de mantenimiento y ubicación de los datos. Lo que asumes es todo lo demás: capacidad, parches, certificados, almacenamiento, monitoreo y recuperación. Dos procesos en el mismo servidor no eliminan el fallo de ese servidor, y un backup solo vale si lo puedes restaurar.

El error típico: creer que on-premise significa "sin autoescalado". Puedes tener contenedores, Kubernetes e infraestructura como código. La diferencia es que escalar necesita recursos disponibles: si el clúster está lleno, una política no crea capacidad que todavía no compraste.

Optaría por esta alternativa cuando la cercanía a sistemas locales, el control o las restricciones verificadas valgan más que delegar la operación. La revisaría si el equipo pierde capacidad de entrega por mantener infraestructura, o si los picos superan una y otra vez lo instalado. Ojo: una exigencia de cumplimiento, por sí sola, no demuestra que cloud esté prohibida.

Cloud con EC2: capacidad programable sin rediseñar tu Java

Este escenario cubre otra necesidad: desplegar una API Java que corre de forma continua y ampliar recursos sin comprar servidores. Mantienes un modelo que tu equipo ya conoce, con procesos persistentes, contenedores y una base relacional. Sirve cuando quieres mover o modernizar un sistema de a poco, sin reescribir su lógica como funciones.

Cognito pone la identidad. WAF y un ALB reciben la petición, ECS organiza los contenedores sobre hosts EC2 y Spring Boot procesa el pedido. RDS PostgreSQL guarda los datos y la outbox; un relay publica los eventos en SQS y un consumidor en ECS los procesa. La API y RDS pueden vivir en subredes privadas mientras el ALB público recibe solo el tráfico permitido.

Aquí está lo que más se confunde: AWS administra la infraestructura física, pero con ECS sobre EC2 tú sigues administrando los hosts, su sistema operativo y la aplicación. RDS y SQS delegan otras capas. Una misma solución mezcla distintos grados de gestión (AWS, responsabilidad compartida en ECS).

Conservas el runtime Java, tus herramientas de diagnóstico y las transacciones locales. Puedes escalar tareas y capacidad con políticas, aunque aprovisionar y arrancar lleva tiempo. Y mover el código no arregla un N+1 ni un índice que falta: la latencia sigue dependiendo de tus consultas.

Elegiría EC2 con ECS si necesito controlar los hosts o tengo requisitos que lo justifiquen. Si solo necesito ejecutar contenedores, evaluaría Fargate primero: también es serverless (AWS, comparación de Fargate y Lambda).

Serverless con Lambda: ejecución por eventos y menos infraestructura

Este escenario permite ejecutar lógica sin aprovisionar los servidores donde corre cada función. Encaja con webhooks, integraciones, procesamiento de archivos y trabajo asíncrono con demanda variable. La idea es delegar la infraestructura y que la capacidad siga a la demanda.

El cliente inicia sesión con Cognito y llama a API Gateway con su access token. El authorizer verifica el JWT y una Lambda en Java ejecuta la lógica; la autorización de negocio sigue en tu código. DynamoDB registra el pedido, Streams captura el cambio y otra Lambda lo publica en SQS, donde un consumidor lo procesa de forma idempotente. Streams pide configurar reintentos y el manejo de registros fallidos (AWS, integración de Lambda con DynamoDB Streams).

DynamoDB es una elección del ejemplo, no una condición. Puedes conservar una base relacional si tu modelo de datos lo pide, siempre que diseñes conexiones y concurrencia para que el escalado de las funciones no la ahogue.

Cuidado: Lambda amplía entornos concurrentes según la demanda, pero dentro de cuotas y de su velocidad de escalado. Crear un entorno nuevo agrega latencia de inicialización, el famoso cold start. Si tienes un SLO exigente, mide los dos casos y evalúa provisioned concurrency o SnapStart cuando apliquen (AWS, concurrencia y ciclo de vida de Lambda).

Optaría por funciones para tareas acotadas, eventos y demanda variable cuando sus límites encajen. Para ejecución prolongada o un control fino del proceso, miraría contenedores. Y delegar servidores no te quita IAM, dependencias, costos, observabilidad ni diseño de datos.

Lo que no cambia: quién llama y qué puede hacer

Muevas el backend a donde lo muevas, la pregunta de seguridad es la misma: quién llama, qué puede hacer y sobre qué datos. En los tres ejemplos uso una SPA en React con OpenID Connect sobre OAuth 2.0, con Authorization Code y PKCE (Auth0).

El ID token le cuenta al cliente el resultado de la autenticación. El access token sirve para pedir acceso a la API que debe recibirlo. No son intercambiables. Y en una SPA no existe un client secret confidencial: su código se entrega al navegador.

La API comprueba firma, algoritmo permitido, emisor, vigencia y audiencia. Las claves públicas del proveedor de identidad se obtienen por JWKS y se pueden cachear, así que validar un JWT no exige consultar al proveedor en cada petición. Spring Security trae soporte de Resource Server para este patrón (Spring Security).

El error típico: confundir autenticado con autorizado. Validar el token dice quién eres. Después, tu aplicación decide si puedes crear o ver ese pedido, mirando organización, dueño y estado. Esa comprobación no puede depender solo de que haya un gateway delante.

Entre servicios pasa lo mismo: en AWS, un rol IAM autoriza a leer de SQS o DynamoDB, pero no reemplaza la identidad del usuario ni sus permisos sobre un pedido.

Lo que tampoco cambia: los problemas distribuidos

Un sistema es distribuido cuando sus partes colaboran a través de una red. Da igual dónde corra: incluso un monolito con la base remota ya tiene comunicación distribuida.

No perder el evento. Si la API guarda el pedido y después publica el mensaje, una caída entre las dos acciones deja el pedido sin aviso. Con outbox, pedido y evento se escriben en la misma transacción local, y un relay publica lo confirmado (AWS Prescriptive Guidance; Chris Richardson). Pero si el relay publica y cae antes de marcarlo como enviado, lo publica otra vez: el consumidor tiene que tolerar duplicados. Outbox no convierte la base y el broker en una sola transacción.

Poner límites a los fallos. Timeouts por dependencia y un presupuesto total de latencia. Reintentos limitados, con backoff y jitter, y solo sobre operaciones idempotentes. Una cola absorbe trabajo, pero hay que vigilar su antigüedad y su ritmo de drenaje, y una DLQ necesita un procedimiento de reproceso.

Medir el servicio completo. El P99 se mide en la entrada de la API y se sigue con trazas hasta las consultas. El promedio esconde inicializaciones, consultas lentas y saturación. Y escalar réplicas no corrige un N+1.

Saber cuánto puedes perder. RTO es el tiempo máximo aceptable para restaurar el servicio; RPO, la ventana máxima de datos que aceptas perder. Multi-AZ ayuda frente a ciertos fallos, pero no reemplaza un plan de recuperación probado (AWS Well-Architected).

Cómo decido entre las tres

No hay un ganador universal. Primero descarto lo que incumple restricciones reales; después comparo el costo y la complejidad de cumplir los objetivos. Esta tabla muestra tendencias, no puntajes:

CriterioOn-premiseCloud con EC2Serverless con Lambda
Dependencias localesVentajoso cerca de equipos y sistemas internosNecesita conectividad; medir la latencia hacia lo localLa conectividad y sus límites tienen que encajar con el flujo
Carga y capacidadEscala dentro de lo instalado o ampliándoloAmplía hosts y tareas según políticasAmplía concurrencia dentro de cuotas; cuidar dependencias
Control del procesoAmplio: hardware, sistema operativo y runtimeHosts y runtime sobre recursos del proveedorMenor; se diseña según el servicio
Operación del equipoInfraestructura física y plataformaSe delega el hardware; quedan hosts y aplicaciónSe delega la ejecución; quedan código, IAM y configuración
Latencia y duraciónDependen de red, capacidad y diseñoCómputo preparado; validar el P99Medir inicialización y límites de la modalidad
PortabilidadDepende de hardware y automatizaciónContenedores trasladables; los servicios gestionados piden adaptaciónLas integraciones con APIs del proveedor piden adaptación
Costo totalInversión, capacidad ociosa, energía y personalCapacidad aprovisionada, servicios, red y operaciónConsumo, capacidad preparada, servicios, red y operación

Y el orden en que hago las preguntas:

  1. Restricciones. Dónde pueden vivir los datos y las dependencias, qué conectividad existe y qué tiene que seguir funcionando sin internet.
  2. Carga. Duración, concurrencia, estacionalidad y tamaño de los payloads.
  3. Objetivos. P99, disponibilidad, RTO y RPO.
  4. Equipo. Experiencia real y capacidad de guardia.
  5. Evidencia. Una prueba representativa, con costos y con fallos.

Para comparar costos, uso la misma carga y el mismo SLO, e incluyo cómputo, datos, solicitudes, transferencia, red, logs, redundancia y horas de operación. Serverless puede convenir con carga intermitente; un uso continuo puede favorecer otra opción. Y "serverless" no significa que todo se cobre por petición: Fargate factura los recursos que piden las tareas mientras corren (AWS, modelo de facturación de Fargate).

Tres necesidades y una combinación razonable

Un sistema de inventario junto a equipos de una planta, que tiene que trabajar aunque se caiga internet. Mantendría el núcleo on-premise y usaría cloud para sincronización o analítica cuando haya enlace. La propuesta tiene que decir cómo se reconcilian los cambios al volver la conexión.

Una API de pedidos en Spring Boot y PostgreSQL, con tráfico continuo y un equipo acostumbrado a contenedores. Un monolito modular en contenedores es una base razonable. Evaluaría Fargate para delegar los hosts, y EC2 si el control o el análisis de costos lo justifican. Conservaría la base relacional: así evito una migración de datos sin un beneficio probado.

Webhooks de terceros que llegan en ráfagas, con mucho tiempo muerto entre medio. Una entrada autenticada, una cola y consumidores Lambda separan la recepción del procesamiento. Antes de dar por aceptada una tarea, la guardaría de forma durable, con deduplicación, límites de concurrencia y un flujo de reproceso.

Muchas veces la mejor respuesta es combinar: un núcleo transaccional local, APIs en cloud y eventos en serverless. Lo importante es decidir dónde termina cada responsabilidad: qué datos son la fuente de verdad, qué comunicación es síncrona y qué puede resolverse con consistencia eventual. Y evitaría que una transacción de usuario cruce varios entornos sin necesidad, porque cada salto suma latencia y nuevas formas de fallar.

Nota: así lo presentaría como arquitecto o tech manager: «Elegimos esta combinación porque cumple estas restricciones y estos SLO. Aceptamos estos costos y riesgos. Mediremos estos indicadores y revisaremos la decisión si cambian la carga, las dependencias o el equipo». Es una propuesta que se puede verificar, no una preferencia por herramientas.

Las fuentes son documentación primaria, consultada el 3 de noviembre de 2026. Los tres casos son ejemplos para razonar, no benchmarks ni precios: antes de implementar, verifica cuotas, runtimes y condiciones de cada servicio.

¿Y tú? ¿Cuál fue la restricción real que terminó decidiendo dónde corre tu sistema: los datos, el equipo, la carga o el costo? Te leo en los comentarios.

Ver versión en inglés