Skip to content

Clase 11 — Taller: Patrones de Diseño en la Práctica y Arquitectura Netflix

Resumen Ejecutivo

Sesión de taller práctica centrada en dos bloques. Primero, resolución de dudas sobre patrones de diseño GoF con ejemplos de código en Java en vivo: Adaptador (dos variantes: composición y herencia), Prototipo y la distinción entre Adaptador y Fachada. El profesor arranca desde una pregunta real del foro sobre el método onClick en Abstract Factory para asentar que los nombres de métodos en los ejemplos son particulares al dominio, no parte del patrón. Segundo, lectura comentada del artículo "Análisis de diseño de microservicios basados en arquitectura cloud en Netflix" (2020), identificando qué patrones de la asignatura aparecen en producción real: Backend for Frontend, Sharding, Tuberías y Filtros, y separación de planos lectura/escritura (CQRS-like). ⚠️ EXAMEN: diferenciar Adaptador de Fachada, reconocer patrones en diagramas de arquitectura real, entender para qué sirve una interfaz/contrato.


Conceptos Clave

  • Patrón Adaptador: Traduce la interfaz de una clase existente a otra que espera el cliente. Permite reutilizar componentes incompatibles sin reescribir el código que los usa. ⚠️ EXAMEN
  • Dos implementaciones del Adaptador:
  • Por composición: el adaptador contiene una instancia del adaptado como atributo privado.
  • Por herencia: el adaptador extiende la clase adaptada y delega a super.
  • Patrón Prototipo: Los objetos implementan una interfaz con método clonar() para copiarse a sí mismos. Útil cuando la construcción es costosa o hay que generar muchas copias con pequeñas variaciones. ⚠️ EXAMEN
  • Patrón Fachada: Clase intermedia que encapsula la complejidad de un subsistema. No traduce interfaces (eso es el Adaptador); reduce acoplamiento y centraliza las dependencias externas. ⚠️ EXAMEN
  • Diferencia Adaptador vs Fachada: El Adaptador adapta una clase concreta para compatibilidad; la Fachada agrupa servicios para reducir acoplamiento. Ambos son "pegamento" entre componentes. ⚠️ EXAMEN
  • Interfaz como contrato: Una interfaz define el comportamiento esperado de múltiples clases. No tiene sentido con una sola implementación; brilla cuando hay varias clases que deben respetar el mismo contrato.
  • Backend for Frontend (BFF): Un backend específico por tipo de cliente/dispositivo. Cada front tiene su back.
  • Microservicios: Componentes distribuidos muy cohesionados y especializados, con su propio almacenamiento y caché. No implica necesariamente contenedores.
  • CDN (Content Delivery Network): Red de servidores geográficamente distribuidos para servir contenido estático (vídeos, imágenes) con baja latencia.
  • Sharding geográfico: Distribuir datos por regiones para servir a usuarios próximos. Netflix lo aplica en los OCA.

Desarrollo del Temario

1. Aclaración: nombres de métodos en Abstract Factory

El método onClick que aparece en el ejemplo del profesor no es un método del patrón Abstract Factory en general, sino específico del dominio del ejemplo (botones de UI). Cada elemento de una familia implementa la interfaz común a ese tipo de elemento:

  • Familia 1 y Familia 2 tienen Botones — todos implementan onClick().
  • Familia 1 y Familia 2 tienen Tablas — todas implementan los métodos de manipulación de tablas.

La fábrica abstracta garantiza que todos los productos de una misma familia son compatibles entre sí.

2. Patrón Adaptador — ejemplo en Java

Problema: tenemos código que usa Person (inglés). Aparece Persona (español) con los mismos métodos pero con otros nombres. Cambiar todos los puntos de uso es costoso y propenso a errores cuando hay parámetros con tipos distintos.

Solución: definir una interfaz común y crear un adaptador.

// Interfaz que todo "tipo persona" debe cumplir
interface PersonInterface {
    void speak();
    void run();
    void shout();
}

Variante 1: adaptador por composición

class PersonaAdaptada implements PersonInterface {
    private Persona persona;

    public PersonaAdaptada() {
        this.persona = new Persona();
    }

    @Override public void speak()  { persona.hablar(); }
    @Override public void run()    { persona.correr(); }
    @Override public void shout()  { persona.gritar(); }
}

Si mañana queremos usar Ragazzo (italiano con métodos parlare(), correre(), gridare()), solo cambiamos el adaptador. El resto del código no se toca.

Variante 2: adaptador por herencia

class PersonaAdaptada2 extends Persona implements PersonInterface {
    @Override public void speak()  { super.hablar(); }
    @Override public void run()    { super.correr(); }
    @Override public void shout()  { super.gritar(); }
}

Cuándo brilla el Adaptador: cuando los métodos tienen parámetros de tipos distintos, o cuando la clase adaptada tiene una API más compleja que el cliente no debe conocer. Un simple renombrado con el IDE no es suficiente en esos casos.

classDiagram
    class PersonInterface {
        <<interface>>
        +speak()
        +run()
        +shout()
    }
    class Person {
        +speak()
        +run()
        +shout()
    }
    class Persona {
        +hablar()
        +correr()
        +gritar()
    }
    class PersonaAdaptada {
        -persona: Persona
        +speak()
        +run()
        +shout()
    }
    PersonInterface <|.. PersonaAdaptada
    PersonaAdaptada o-- Persona
    PersonInterface <|.. Person

3. Patrón Prototipo — ejemplo en Java

Problema: crear muchos objetos similares con constructores complejos (múltiples parámetros: nombre, edad, raza, altura...).

Solución: que el propio objeto sepa clonarse.

interface Cloneable {
    Object clonar();
}

class PersonaAdaptada implements PersonInterface, Cloneable {
    // atributos: nombre, altura, etc.

    @Override
    public PersonaAdaptada clonar() {
        // tiene acceso directo a sus propios atributos privados
        PersonaAdaptada copia = new PersonaAdaptada();
        // copiar atributos
        return copia;
    }
}

Permite crear copias con pequeñas variaciones (p. ej. distinta altura) sin repetir toda la lógica de construcción en el código cliente.

4. Patrón Fachada — ejemplo en Java

Problema: una clase Cliente está muy acoplada a múltiples servicios de librerías externas. Si cambia alguno, hay que refactorizar en muchos puntos.

Solución: extraer toda la lógica de interacción con esos servicios a una clase Fachada.

class Fachada {
    private Servicio1 s1 = new Servicio1();
    private Servicio2 s2 = new Servicio2();
    private Servicio3 s3 = new Servicio3();

    public void generarInformeVentas() {
        s1.servicio();
        s2.servicio();
        s1.servicio();
        // lógica de orquestación encapsulada aquí
    }

    public void generarNominasEmpleados() {
        s2.servicioB();
        s3.servicioA();
    }
}

class Cliente {
    public void procesar() {
        Fachada api = new Fachada();
        api.generarInformeVentas();
        api.generarNominasEmpleados();
    }
}

El Cliente queda expresivo y desacoplado. Si cambia un servicio externo, solo se toca Fachada.

Diferencia clave con Adaptador: ⚠️ EXAMEN

Adaptador Fachada
Objetivo Compatibilidad de interfaces Reducir acoplamiento
Qué hace Traduce métodos de una clase Orquesta un subsistema complejo
Impone interfaz No (más conceptual)
Analogía Traductor API propia a medida

5. Artículo Netflix — patrones de la asignatura en producción

El artículo analizado ("Análisis de diseño de microservicios basados en arquitectura cloud en Netflix", 2020) ilustra cómo los patrones estudiados aparecen en sistemas reales a gran escala.

5.1 Contexto y motivación

  • Netflix representa >15% del ancho de banda global de Internet.
  • En 2008 sufrió una interrupción de 3 días por fallo en sus propios datacenters (único punto de fallo).
  • Decisión: migrar a AWS + pasar de monolito a microservicios.
  • Razón para AWS: bases de datos fiables, almacenamiento a gran escala, múltiples datacenters globales. Netflix se centra en su negocio core (streaming), no en gestionar infraestructura.

5.2 Arquitectura de alto nivel

Tres componentes principales:

graph LR
    C[Clientes<br/>Web / iOS / Android / TV] -->|1. Solicitud reproducción| B[Backend<br/>Amazon Web Services]
    B -->|2. Lista de OCAs óptimos| C
    C -->|3. Streaming de vídeo| CDN[CDN Open Connect<br/>OCAs globales]
    B -->|Noche: sincronización de vídeos| CDN

5.3 Patrón Backend for Frontend (BFF) ⚠️ EXAMEN

Cada tipo de dispositivo tiene su propio backend:

graph TD
    TV[App TV] --> BFF_TV[Back TV]
    iOS[App iOS] --> BFF_iOS[Back iOS]
    AND[App Android] --> BFF_AND[Back Android]
    WEB[Navegador] --> BFF_WEB[Back Web]
    BFF_TV & BFF_iOS & BFF_AND & BFF_WEB --> MS[Microservicios<br/>compartidos]

Razón: los patrones de uso son distintos por dispositivo (TV: picos noche/fines de semana; móvil: commuting). Casos de uso distintos → backends distintos.

5.4 Microservicios

  • Cada función tiene su propio microservicio con su caché y almacenamiento: autenticación, perfil, facturación, transcodificación, subtítulos, recomendaciones...
  • Si falla el microservicio de login, los usuarios que ya están viendo películas no se enteran.
  • Escalado horizontal solo del servicio que lo necesita.
  • Los microservicios no implican contenedores — los contenedores son una tecnología de implementación, no el concepto arquitectónico.

5.5 CDN Open Connect y Sharding geográfico

  • OCA (Open Connect Appliances): servidores propios de Netflix instalados en los ISPs y puntos de intercambio de cada país.
  • No todos los OCAs tienen todos los contenidos — distribución similar a Sharding.
  • El servicio Steering (en AWS) decide qué OCAs sirven cada contenido a cada cliente según IP, ISP y latencia medida.
  • Cada noche, los nuevos vídeos transcodificados en S3 (AWS) se sincronizan a los OCAs relevantes.
  • Los OCAs usan arquitectura peer-to-peer para copiarse contenidos entre sí.

5.6 Tuberías y Filtros en transcodificación

La plataforma Cosmos de Netflix implementa el pipeline de transcodificación de vídeo con el patrón Tuberías y Filtros: cada etapa (ingest, codificación, empaquetado, validación...) es un filtro independiente conectado por tuberías.

5.7 Flujo de reproducción (paso a paso)

  1. El cliente pulsa "Play" y envía solicitud al backend (AWS).
  2. El balanceador de carga (ELB) la reenvía a la API Gateway (Zuul).
  3. La API Gateway aplica filtros de seguridad/lógica de negocio y enruta al microservicio de reproducción.
  4. El servicio de reproducción valida: plan del suscriptor, licencia del contenido en ese país.
  5. Consulta al servicio Steering → devuelve lista de OCAs con ese contenido, ordenados por proximidad/calidad.
  6. El cliente prueba la latencia a esos OCAs y elige el mejor.
  7. El cliente descarga el vídeo directamente del OCA seleccionado.

Preguntas de Autoevaluación

  1. ¿Cuáles son las dos implementaciones posibles del patrón Adaptador? ¿Qué diferencia hay entre ellas?
  2. ⚠️ EXAMEN: Explica la diferencia entre Adaptador y Fachada. ¿Cuándo usarías cada uno?
  3. ¿Para qué sirve el patrón Prototipo? Da un ejemplo de cuándo copiar un objeto directamente desde el cliente sería problemático.
  4. ¿Por qué los patrones de diseño no son obligatorios? ¿Cuándo tiene sentido aplicarlos y cuándo no?
  5. ¿Qué es una interfaz como contrato? ¿Por qué carece de sentido con una única clase que la implementa?
  6. ⚠️ EXAMEN: ¿Qué es el patrón Backend for Frontend? Dibuja un diagrama esquemático. ¿Por qué Netflix lo usa?
  7. ¿Por qué migró Netflix de su propia infraestructura a AWS? ¿Qué problema resolvió esa decisión?
  8. ¿Qué diferencia hay entre un sistema monolítico y una arquitectura de microservicios? ¿Qué ventaja tiene para el escalado?
  9. ¿Qué es un OCA en la arquitectura de Netflix? ¿A qué patrón de la asignatura se parece su distribución geográfica?
  10. ¿Qué patrón de arquitectura se usa en el pipeline de transcodificación de vídeo de Netflix?
  11. ¿Por qué no todos los OCAs tienen todos los contenidos de Netflix? ¿Qué implicación tiene esto para las licencias geográficas?