Repaso — Quiz 4

Programación Funcional · BFF · Docker

Repaso — Quiz 4

Temas a repasar:

  1. Programación Funcional en Java
  2. Backend for Frontend (BFF)
  3. Docker & Docker Compose

Tip

Controles del Quiz: Presiona c para verificar tu respuesta, q para reiniciar una pregunta.

Programación Funcional en Java

Escribiendo código declarativo con lambdas, Streams y Optional.

Interfaces Funcionales

Una interfaz funcional es una interfaz con un solo método abstracto (SAM).

Interfaz Método Entrada → Salida Ejemplo
Function<T,R> apply(T) T → R Transformar un objeto
Predicate<T> test(T) T → boolean Filtrar elementos
Consumer<T> accept(T) T → void Imprimir, guardar
Supplier<T> get() () → T Crear objetos
UnaryOperator<T> apply(T) T → T Modificar mismo tipo
BiFunction<T,U,R> apply(T,U) (T,U) → R Combinar dos valores
// Ejemplo
Function<String, Integer> longitud = s -> s.length();
Predicate<Integer> esPar = n -> n % 2 == 0;
Consumer<String> imprimir = s -> System.out.println(s);
Supplier<List<String>> crearLista = ArrayList::new;

Expresiones Lambda

Una lambda es una función anónima que implementa una interfaz funcional.

Sintaxis completa:

(String nombre) -> {
    return "Hola " + nombre;
}

Sintaxis simplificada:

nombre -> "Hola " + nombre
// Lambda vs Clase anónima
// ANTES (Java 7)
Collections.sort(lista, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

// DESPUÉS (Java 8+)
lista.sort((a, b) -> a.compareTo(b));
// O con Method Reference
lista.sort(String::compareTo);

Nota

Method Reference (::) es un atajo para lambdas que simplemente delegan a un método existente.

Stream API

Un Stream es una secuencia de elementos que soporta operaciones de procesamiento declarativas.

graph LR
    SRC["Fuente<br/>(Collection, Array)"] --> INT["Operaciones Intermedias<br/>filter, map, sorted, distinct"]
    INT --> TERM["Operación Terminal<br/>collect, forEach, reduce, count"]
    
    style SRC fill:#8be9fd,color:#000
    style INT fill:#ffb86c,color:#000
    style TERM fill:#50fa7b,color:#000

Tipo Operaciones Característica
Intermedias filter, map, flatMap, sorted, distinct, peek Lazy — no ejecutan hasta terminal
Terminales collect, forEach, reduce, count, findFirst, anyMatch Ejecutan el pipeline

Stream API — Ejemplos

List<Product> products = getProducts();

// Filtrar, transformar y recolectar
List<String> nombresCaros = products.stream()
    .filter(p -> p.getPrice() > 1000)       // Predicate
    .map(Product::getName)                    // Function
    .sorted()                                 // Comparable
    .collect(Collectors.toList());            // Terminal

// Reducir a un valor
double totalVentas = products.stream()
    .mapToDouble(Product::getPrice)
    .sum();

// Agrupar por categoría
Map<String, List<Product>> porCategoria = products.stream()
    .collect(Collectors.groupingBy(Product::getCategory));

// FlatMap: aplanar listas anidadas
List<String> todasLasTags = products.stream()
    .flatMap(p -> p.getTags().stream())       // Stream<Stream<String>> → Stream<String>
    .distinct()
    .collect(Collectors.toList());

Optional

Optional<T> es un contenedor que puede o no contener un valor. Evita NullPointerException.

Sin Optional:

Product p = findById(id);
if (p != null) {
    String name = p.getName();
    if (name != null) {
        return name.toUpperCase();
    }
}
return "UNKNOWN";

Con Optional:

return findById(id)
    .map(Product::getName)
    .map(String::toUpperCase)
    .orElse("UNKNOWN");
Método Descripción
of(value) Crea Optional (lanza si null)
ofNullable(value) Crea Optional (vacío si null)
isPresent() ¿Tiene valor?
map(Function) Transforma el valor si existe
flatMap(Function) Como map, pero la función retorna Optional
orElse(default) Valor por defecto si vacío
orElseThrow() Lanza excepción si vacío

Quiz: Programación Funcional

¿Cuál interfaz funcional recibe un argumento y retorna un boolean?

  • Function<T, Boolean>
  • Predicate<T>
  • Consumer<T>
  • Supplier<T>

¿Qué imprime este código?

List<Integer> nums = List.of(1, 2, 3, 4, 5);
long result = nums.stream()
    .filter(n -> n % 2 == 0)
    .count();
System.out.println(result);
  • 5
  • 2
  • 3
  • Error de compilación

¿Cuál es la diferencia entre map y flatMap en Streams?

  • map es para primitivos y flatMap para objetos
  • map transforma 1:1, flatMap aplana streams anidados
  • Son sinónimos, hacen lo mismo
  • flatMap solo funciona con Optional

¿Qué retorna este código?

Optional<String> opt = Optional.ofNullable(null);
String result = opt.orElse("default");
  • null
  • "default"
  • NullPointerException
  • Optional.empty()

¿Cuál operación de Stream es terminal?

  • filter()
  • map()
  • sorted()
  • collect()

¿Qué tipo de Method Reference es String::length?

  • String::length es una referencia a un constructor
  • String::length es una referencia a un método de instancia de un tipo arbitrario
  • String::length es una referencia a un método estático
  • String::length es una referencia a un método de instancia de un objeto particular

¿Qué hace reduce() en un Stream?

  • Filtra los elementos que cumplen una condición
  • Transforma cada elemento del Stream
  • Combina todos los elementos del Stream en un solo resultado usando una función acumuladora
  • Ordena los elementos del Stream

¿Qué retorna Collectors.groupingBy()?

  • List<T>
  • Set<T>
  • Map<K, List<T>>
  • Optional<T>

¿Cuál es la diferencia entre peek() y forEach()?

  • Hacen exactamente lo mismo, son sinónimos
  • peek() es intermedia y retorna un Stream; forEach() es terminal y no retorna nada
  • forEach() es intermedia y peek() es terminal
  • peek() modifica los elementos y forEach() solo los lee

¿Qué pasa si usas un Stream después de una operación terminal?

  • El Stream se reinicia y puedes reutilizarlo
  • No pasa nada, puedes seguir usándolo
  • Se lanza IllegalStateException porque el Stream ya fue consumido
  • Se crea automáticamente un nuevo Stream

¿Qué diferencia hay entre Optional.map() y Optional.flatMap()?

  • No hay diferencia, hacen lo mismo
  • map() lanza excepción si el Optional está vacío; flatMap() no
  • map() envuelve el resultado en Optional; flatMap() espera que la función ya retorne Optional y aplana el resultado
  • flatMap() solo funciona con listas dentro del Optional

¿Qué es una variable ‘effectively final’ en el contexto de lambdas?

  • Una variable declarada como final explícitamente
  • Una variable que puede cambiar su valor dentro de la lambda
  • Una variable que no se reasigna después de su inicialización, aunque no tenga el modificador final
  • Una variable estática de la clase

¿Qué interfaz funcional recibe DOS argumentos y retorna un resultado?

  • Function<T,R>
  • UnaryOperator<T>
  • BiFunction<T,U,R>
  • BinaryOperator<T>

¿Cuál es la diferencia entre findFirst() y findAny() en Streams?

  • Son idénticos, siempre retornan el mismo resultado
  • findFirst() retorna el primer elemento en orden; findAny() puede retornar cualquier elemento, útil en streams paralelos
  • findFirst() lanza excepción si el Stream está vacío; findAny() retorna null
  • findAny() retorna todos los elementos; findFirst() solo uno

¿Qué hace Collectors.toMap(keyMapper, valueMapper)?

  • Agrupa elementos por clave en listas
  • Crea un Map donde cada elemento se convierte en una entrada clave-valor
  • Convierte un Map existente en un Stream
  • Ordena los elementos y los pone en un TreeMap

Backend for Frontend (BFF)

Optimizando la comunicación entre clientes y microservicios.

API Gateway — Repaso

Un API Gateway es un punto de entrada único para todos los clientes.

graph TB
    WEB[Web App] --> GW
    MOB[Mobile App] --> GW
    EXT[Third Party] --> GW
    
    subgraph "API Gateway"
        GW[Gateway]
        AUTH[Auth]
        RL[Rate Limiting]
        CACHE[Cache]
    end
    
    GW --> S1[Product Service]
    GW --> S2[Order Service]
    GW --> S3[Inventory Service]

    style GW fill:#bd93f9,color:#fff
    style AUTH fill:#50fa7b,color:#000
    style RL fill:#ffb86c,color:#000
    style CACHE fill:#8be9fd,color:#000

Responsabilidades: Routing, Auth, Rate Limiting, Load Balancing, Circuit Breaking.

¿Qué es BFF?

Backend for Frontend: variante del API Gateway donde cada tipo de cliente tiene su propio backend optimizado.

graph TB
    WEB["Web App"] --> BFF_WEB["BFF Web<br/>(datos ricos, paginación)"]
    MOB["Mobile App"] --> BFF_MOB["BFF Mobile<br/>(datos mínimos, offline)"]
    IOT["IoT"] --> BFF_IOT["BFF IoT<br/>(comandos simples)"]
    
    BFF_WEB --> S1[Product Service]
    BFF_WEB --> S2[Order Service]
    BFF_MOB --> S1
    BFF_MOB --> S3[Inventory Service]
    BFF_IOT --> S3

    style BFF_WEB fill:#bd93f9,color:#fff
    style BFF_MOB fill:#ff79c6,color:#fff
    style BFF_IOT fill:#ffb86c,color:#000

Tip

Cada BFF adapta las respuestas a las necesidades de su cliente: la web recibe más datos, el móvil recibe payloads más ligeros.

API Gateway vs BFF

Característica API Gateway BFF
Enfoque Único punto de entrada Múltiples gateways por cliente
Adaptabilidad “Talla única” A medida para cada cliente
Propiedad Equipo de Plataforma Equipo de Frontend/Producto
Ventaja Simplicidad y centralización Optimización de UX y performance
Riesgo Cuello de botella Duplicación de lógica
Evolución Evoluciona de forma centralizada Cada BFF evoluciona independiente

Nota

El BFF no reemplaza al API Gateway; a menudo coexisten. El Gateway maneja cross-cutting concerns (SSL, Auth) y enruta a los BFFs.

¿Cuándo usar BFF?

SÍ usar BFF cuando:

  • Múltiples tipos de clientes con necesidades muy diferentes
  • El equipo de frontend necesita autonomía
  • Se requiere optimizar payloads por plataforma
  • La web necesita SSR y el móvil REST simple

NO usar BFF cuando:

  • Solo tienes un tipo de cliente
  • Los clientes consumen datos similares
  • Equipo pequeño (overhead de mantener N backends)
  • No tienes suficiente complejidad en la capa de API

Quiz: BFF

¿Qué significa BFF en el contexto de microservicios?

  • Best Friend Forever
  • Backend for Frontend
  • Backend Full Framework
  • Browser First Framework

¿Cuál es la principal ventaja del BFF sobre un API Gateway único?

  • Es más fácil de implementar
  • Consume menos recursos del servidor
  • Optimiza las respuestas para cada tipo de cliente
  • Reemplaza totalmente al API Gateway

¿Quién típicamente es responsable (owner) de un BFF?

  • El equipo de DevOps
  • El equipo de Base de Datos
  • El equipo de Frontend/Producto
  • El equipo de Seguridad

¿Cuándo NO es recomendable usar el patrón BFF?

  • Cuando tienes clientes web y mobile con necesidades muy diferentes
  • Cuando el equipo de frontend necesita autonomía
  • Cuando solo tienes un tipo de cliente y un equipo pequeño
  • Cuando necesitas optimizar el payload para dispositivos móviles

Si ya tengo un API Gateway, necesito quitar el Gateway para usar BFF?

  • Si, el BFF reemplaza completamente al API Gateway
  • No, pero el BFF debe ir ANTES del API Gateway
  • No, BFF y API Gateway coexisten; el Gateway maneja cross-cutting concerns y enruta a los BFFs
  • Si, tener ambos causa conflictos de routing

Como se comunica un BFF con los microservicios downstream?

  • Solo mediante bases de datos compartidas
  • Mediante llamadas HTTP/REST o gRPC a las APIs de los microservicios
  • El BFF accede directamente a las bases de datos de los microservicios
  • Los microservicios envían datos al BFF automáticamente

Que pasa si dos BFFs necesitan la misma logica de negocio?

  • Cada BFF duplica la lógica, es inevitable
  • Se unifican los BFFs en uno solo
  • La lógica compartida debe estar en los microservicios downstream, no en los BFFs
  • Se crea un ‘BFF compartido’ que ambos consumen

Que problema resuelve el BFF con respecto al over-fetching?

  • El BFF cachea datos para reducir llamadas
  • El BFF comprime los datos para reducir el tamaño
  • El BFF adapta y filtra la respuesta para enviar solo los datos que cada cliente necesita
  • El BFF pagina automáticamente todas las respuestas

Cuantos BFFs deberia tener un proyecto tipicamente?

  • Solo uno, para mantener la simplicidad
  • Uno por cada microservicio backend
  • Uno por cada tipo de cliente o experiencia de usuario
  • Tantos como developers haya en el equipo

Que tipo de logica debe contener un BFF?

  • Lógica de negocio core del dominio
  • Lógica de agregación de datos, transformación de formatos y adaptación de respuestas para el cliente
  • Lógica de autenticación y autorización
  • Lógica de persistencia y acceso a bases de datos

Como afecta el patron BFF al deployment?

  • Todos los BFFs se despliegan juntos en un solo artefacto
  • Cada BFF se despliega independientemente, lo que permite releases por cliente
  • Los BFFs se despliegan como parte de los microservicios backend
  • Solo se despliegan cuando cambia el frontend

Que pasa si un microservicio downstream falla y el BFF depende de el?

  • El BFF también falla completamente
  • El BFF debe implementar patrones de resiliencia como Circuit Breaker, fallbacks y respuestas degradadas
  • El API Gateway se encarga de manejar ese fallo
  • El cliente frontend maneja el error directamente

Como beneficia el BFF al rendimiento de aplicaciones moviles?

  • El BFF comprime automáticamente todas las respuestas
  • Reduce el numero de llamadas HTTP y el tamaño del payload adaptándolo al móvil
  • El BFF almacena datos localmente en el dispositivo móvil
  • No hay beneficio específico para móvil

Cual es el principal anti-patron al implementar BFF?

  • Tener un BFF por cada tipo de cliente
  • Crear un BFF genérico que sirva a todos los clientes por igual
  • Que el equipo de frontend sea dueño del BFF
  • Desplegar cada BFF de forma independiente

Docker — Repaso

Contenedorización y orquestación con Docker Compose.

Dockerfile — Conceptos Clave

Instrucción Qué hace Ejemplo
FROM Imagen base FROM eclipse-temurin:21-jre
WORKDIR Directorio de trabajo WORKDIR /app
COPY Copiar archivos COPY build/libs/*.jar app.jar
RUN Ejecutar en build time RUN apt-get update
ENV Variable de entorno ENV JAVA_OPTS="-Xmx512m"
EXPOSE Puerto documentado EXPOSE 8080
CMD Comando al iniciar CMD ["java", "-jar", "app.jar"]
ENTRYPOINT Ejecutable principal ENTRYPOINT ["java"]
ARG Variable solo en build ARG JAR_VERSION=1.0

Advertencia

ARG solo está disponible en tiempo de build. ENV persiste en el contenedor en ejecución.

Multi-Stage Build

Separar compilación de ejecución para imágenes más livianas.

graph LR
    subgraph "Stage 1: Builder (~800MB)"
        JDK[JDK + Gradle]
        SRC[Código Fuente]
        BUILD["./gradlew bootJar"]
        JAR["app.jar"]
    end
    
    subgraph "Stage 2: Runtime (~280MB)"
        JRE[JRE Only]
        JARR["COPY --from=builder app.jar"]
    end
    
    JDK --> SRC
    SRC --> BUILD
    BUILD --> JAR
    JAR -.->|"COPY --from=builder"| JARR

    style JDK fill:#ff5555,color:#fff
    style JRE fill:#50fa7b,color:#000
    style JARR fill:#50fa7b,color:#000

Tip

Con multi-stage builds, la imagen final no incluye JDK, Gradle, ni código fuente — solo el JRE y el JAR.

Docker Compose — Repaso

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=jdbc:postgresql://db:5432/arka
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s

volumes:
  pgdata:

Conceptos clave:

  • services: cada contenedor
  • build: construir desde Dockerfile
  • image: usar imagen existente
  • ports: host:container
  • environment: variables de entorno
  • depends_on: orden de inicio
  • volumes: persistencia
  • healthcheck: verificar salud
  • networks: redes personalizadas

Volúmenes y Networking

Tipos de Volúmenes:

Tipo Uso
Named Volume Persistencia (prod)
Bind Mount Desarrollo (hot-reload)
tmpfs Datos temporales en RAM
# Named Volume
-v pgdata:/var/lib/postgresql/data

# Bind Mount
-v $(pwd)/src:/app/src

Networking:

graph TB
    subgraph "docker network"
        APP[app] <-->|DNS: db| DB[postgres]
        APP <-->|DNS: kafka| KF[kafka]
    end

  • Redes custom → resolución DNS por nombre
  • docker network create mi-red
  • En compose: automáticamente cada servicio se resuelve por nombre

Comandos Esenciales

# Docker
docker build -t mi-app:1.0 .           # Construir imagen
docker run -d -p 8080:8080 mi-app:1.0   # Ejecutar contenedor
docker ps                                # Listar contenedores activos
docker logs -f <container>               # Ver logs en tiempo real
docker exec -it <container> /bin/bash    # Entrar al contenedor

# Docker Compose
docker compose up -d                     # Iniciar todos los servicios
docker compose down                      # Detener y eliminar
docker compose logs -f <service>         # Logs de un servicio
docker compose ps                        # Estado de servicios
docker compose up -d --scale app=3       # Escalar servicio

Quiz: Docker

¿Cuál es la diferencia entre una imagen y un contenedor en Docker?

  • Son lo mismo, son sinónimos
  • La imagen es una plantilla de solo lectura; el contenedor es una instancia en ejecución de esa imagen
  • El contenedor es una imagen comprimida
  • La imagen solo existe en Docker Hub

¿Para qué sirve un Multi-Stage Build?

  • Para ejecutar múltiples aplicaciones en un contenedor
  • Para crear múltiples contenedores a la vez
  • Para separar la fase de compilación de la de ejecución y reducir el tamaño de la imagen final
  • Para construir imágenes en paralelo

¿Qué hace EXPOSE 8080 en un Dockerfile?

  • Abre automáticamente el puerto 8080 al host
  • Bloquea el tráfico en el puerto 8080
  • Documenta que la aplicación dentro del contenedor escucha en el puerto 8080
  • Configura el firewall del contenedor

¿Cuál comando de Compose escala un servicio a 3 instancias?

  • docker compose replicate app 3
  • docker compose run --instances 3 app
  • docker compose up -d --scale app=3
  • docker compose start app app app

¿Cuál es la diferencia entre ARG y ENV en un Dockerfile?

  • No hay diferencia, son intercambiables
  • ARG se usa en el contenedor y ENV solo en el build
  • ARG solo está disponible en tiempo de build; ENV persiste en el contenedor en ejecución
  • ENV se define en docker-compose y ARG en Dockerfile

Cual es la diferencia entre COPY y ADD en un Dockerfile?

  • Son exactamente iguales, hacen lo mismo
  • COPY solo copia archivos locales; ADD además puede descargar URLs y extraer archivos tar automáticamente
  • ADD es más rápido que COPY
  • COPY funciona solo en Linux y ADD en cualquier plataforma

Cual es la diferencia entre CMD y ENTRYPOINT en un Dockerfile?

  • Son lo mismo, se pueden usar indistintamente
  • CMD define el comando por defecto que puede ser sobreescrito; ENTRYPOINT define el ejecutable principal que siempre se ejecuta
  • ENTRYPOINT es para desarrollo y CMD para producción
  • CMD se ejecuta en build time y ENTRYPOINT en runtime

Como funciona el cache de capas en Docker?

  • Docker no usa cache, siempre reconstruye todo
  • Cada instrucción del Dockerfile crea una capa; si una capa cambia, todas las capas posteriores se reconstruyen
  • Docker cachea solo la imagen final, no las capas intermedias
  • El cache solo funciona si usas la misma máquina para construir

Para que sirve el archivo .dockerignore?

  • Para listar los archivos que Docker debe incluir en la imagen
  • Para excluir archivos y directorios del contexto de build, reduciendo el tamaño y tiempo de build
  • Para configurar las redes del contenedor
  • Para definir variables de entorno del contenedor

Que hace el healthcheck en Docker Compose?

  • Reinicia automáticamente el contenedor si falla
  • Verifica periódicamente si el servicio dentro del contenedor está funcionando correctamente
  • Monitorea el uso de CPU y memoria del contenedor
  • Envía alertas al administrador cuando un servicio falla

Cual es el ciclo de vida de un contenedor Docker?

  • Solo tiene dos estados: running y stopped
  • Created, Running, Paused, Stopped, Removed
  • Build, Deploy, Run, Destroy
  • Pull, Install, Execute, Uninstall

Que tipos de redes soporta Docker?

  • Solo un tipo: la red por defecto
  • bridge, host, overlay, none, y macvlan
  • Solo bridge y host
  • TCP, UDP y HTTP

Que hace depends_on con condition: service_healthy en Docker Compose?

  • Solo define el orden de inicio de los contenedores
  • Espera a que el servicio dependido esté healthy (pase su healthcheck) antes de iniciar el servicio dependiente
  • Reinicia el servicio dependiente si el otro falla
  • Crea una red privada entre los dos servicios

Cual es la diferencia entre una imagen con tag latest y una con version especifica?

  • latest siempre tiene las últimas correcciones y es más segura
  • No hay diferencia, son idénticas
  • latest es un tag mutable que puede cambiar; un tag de version especifica es fijo y predecible
  • El tag latest no se puede usar en Dockerfiles

Para que se usa docker exec -it?

  • Para crear un nuevo contenedor interactivo
  • Para ejecutar un comando dentro de un contenedor que ya está corriendo, con terminal interactiva
  • Para detener un contenedor de forma interactiva
  • Para ver los logs del contenedor en tiempo real

Resumen

Lo que debes saber para el Quiz 4

Programación Funcional

  • Interfaces funcionales (Function, Predicate, Consumer, Supplier)
  • Lambdas y Method References
  • Stream API (filter, map, flatMap, collect, reduce)
  • Optional y sus métodos
  • Operaciones intermedias vs terminales

BFF

  • Qué es API Gateway
  • BFF = Backend for Frontend
  • Diferencias API Gateway vs BFF
  • Cuándo usar y cuándo NO
  • BFF coexiste con Gateway
  • Quién es owner del BFF

Docker

  • Imagen vs Contenedor
  • Dockerfile (FROM, COPY, RUN, CMD, ENV, ARG, EXPOSE)
  • Multi-Stage Builds
  • Docker Compose (services, volumes, networks)
  • Volúmenes (Named, Bind Mount)
  • Networking y DNS
  • Comandos esenciales

Tip

¡Éxitos en el quiz!