10 - Serverless: Lambdas en local

El monolito modular con un único despliegue cubre el flujo principal de la aplicación, pero no toda la relación con el exterior. Los proveedores de nube ofrecen FaaS (*Function as a Service*): funciones pequeñas, sin servidor que administrar, activadas por eventos y facturadas por ejecución. En AWS el servicio se llama Lambda. Este tema estudia el modelo y, sin cuenta en la nube, cómo desarrollarlo en local con LocalStack en docker.

En un FaaS, el proveedor provisiona, escala y ejecuta la función en respuesta a un evento. La función se escribe como un handler con una entrada y una salida; se despliega el código, no una aplicación permanente.

Característica Función de nube
Ciclo de vida Se activa por eventos; no está siempre encendida
Estado Nulo: debe caber en la llamada; lo persistente queda en el almacén o en la cola que lo activó
Escalado Gestionado por el proveedor según la carga
Configuración Memoria y timeout fijados por función; secretos por variables de entorno
Facturación Por ejecución y recursos consumidos
Una función “fría” sufre cold start: la primera invocación paga la inicialización del runtime. En Java, las herramientas de snapshots la reducen bastante, pero el diseño prudente mantiene el arranque ligero (sin inicializar frameworks completos por invocación).
Situación Diseño que encaja
El flujo principal del negocio El monolito modular y sus módulos
Tareas aisladas, activadas por un evento, con picos irregulares Una función

Comparada con una tarea dentro del monolito, una función aporta independencia de despliegue y de escalado, pero su ejecución vive en otro proceso: comunicarse exige contratos explícitos y gestión de errores, como con cualquier servicio aparte.

Una Lambda es una función registrada en AWS con:

  • Un runtime: por ejemplo Java 17 o 21.
  • Un handler: la clase y el método de entrada.
  • Activadores (*triggers*): quién invoca la función — API Gateway (petición HTTP), SQS (mensaje en cola), S3 (fichero subido) o un temporizador (EventBridge).

El evento llega como datos (para los activadores HTTP, habitualmente JSON) y la respuesta sale como datos. La función no sirve páginas ni mantiene conexiones persistentes: procesa y devuelve.

public class FraudCheckHandler
        implements RequestHandler<ChargeEvent, FraudDecision> {

    public FraudDecision handleRequest(ChargeEvent event, Context context) {
        context.getLogger().log("Revisando " + event.transactionId());
        return fraudRules().evaluate(event.amount(), event.country());
    }
}

El handler solo trata el ChargeEvent y devuelve la decisión. El SDK de AWS no entra en el handler salvo que la propia función necesite llamar a otros servicios (consultar una cola, leer un fichero).

Los secretos y los límites de recursos se configuran en el entorno de la función (variables de entorno del proveedor), nunca dentro del código, igual que en el resto de la aplicación.

LocalStack es un contenedor que replica las APIs de AWS en local detrás de un único endpoint (puerto 4566), sin credenciales ni cuenta reales. La función se registra y se activa contra la API emulada igual que contra la real:

services:
  aws:
    image: localstack/localstack
    ports:
      - "4566:4566"
    environment:
      - SERVICES=lambda,sqs

Con el CLI configurado contra el endpoint local (awslocal), cada operación es la misma que se lanzaría contra AWS real.

El entorno emulado también ejecuta las funciones Java: la función corre de verdad dentro del contenedor. Es desarrollo local, no un simulador de respuestas.

La función se empaqueta como un jar con sus dependencias y se registra en LocalStack:

mvn package

awslocal lambda create-function \
  --function-name fraud-check \
  --runtime java17 \
  --handler com.example.FraudCheckHandler::handleRequest \
  --zip-file fileb://target/fraud-check.jar \
  --timeout 30 --memory-size 512

awslocal lambda invoke \
  --function-name fraud-check \
  --payload file://event.json result.json

La invocación directa devuelve la salida del handler: la forma más simple de comprobar el comportamiento. Para activarla vía HTTP, una function URL (el equivalente mínimo de API Gateway) la expone:

awslocal lambda create-function-url-config \
  --function-name fraud-check --auth-type NONE

curl -X POST <url-generada> \
  -H "Content-Type: application/json" -d @charge.json

Y para desacoplar el disparo, la función se vincula a una cola y se ejecuta con cada mensaje que llega:

awslocal sqs create-queue --queue-name charge-events

awslocal lambda create-event-source-mapping \
  --function-name fraud-check \
  --event-source-arn <arn-de-la-cola>

El monolito publica en la cola; el activador recoge el mensaje y ejecuta la función.

Los pasos son los mismos que contra la nube real; solo cambian los endpoints. Eso permite ensayar el despliegue en local antes de repetirlo en producción (pregunta si hay que hacerlo luego).

La función es un componente externo al monolito: no importa sus modelos ni consulta su base de datos. Se comunica con la misma disciplina de API First: el formato del evento es un contrato estable, y cualquier dato del negocio que la función necesite se lo pide a la API pública por HTTP, como cualquier otro cliente.

Monolito modularSQS / brokerLambda(handler)evento externalizadoactiva la funciónconsulta la API pública(si necesita datos)

Colaboración Mecanismo
El flujo principal encarga trabajo asíncrono Evento en SQS (o mensaje del broker del tema 08)
La función necesita datos del negocio Llamada HTTP a la API pública del monolito
El monolito necesita el resultado La función devuelve en la cola de respuesta o expone function URL
El handler debe mantenerse mínimo y sin lógica de negocio duplicada. Las reglas del negocio del monolito las aplica el monolito o su API pública; la función aporta solo su propio comportamiento.
  • Handler: pruebas unitarias sin dependencias: entrada, reglas propias y salida. Son las más baratas y la base.
  • Adaptadores de infraestructura (cola, fichero, etc.): probarlos contra LocalStack o Testcontainers, igual que cualquier pieza técnica.
  • Integración local: prueba de humo contra el compose: activar (HTTP o cola) y comprobar el resultado, sin despliegue real.

class FraudCheckHandlerTest {

    @Test
    void rejectsChargesFromSanctionedCountry() {
        var event = new ChargeEvent("tx-1", new BigDecimal("30"), "KP");

        var decision = new FraudCheckHandler().handleRequest(event, null);

        assertEquals(FraudDecision.rejected(), decision);
    }
}

El Context puede sustituirse por un doble simple: no aporta a la prueba del handler; la plataforma solo participa en las pruebas de integración.

La lógica del handler es una clase ordinaria independiente de la plataforma. Las pruebas de unidad se centran en las reglas y no necesitan levantar LocalStack; el entorno emulado se reserva para dar identidad real: registro, activadores y respuestas de la plataforma.
  • clase/daw/dws/1eval/lambda.txt
  • Última modificación: 2026/10/21 11:35
  • por cesguiro