13. Facade (Fachada) - Patrón Estructural

El patrón Facade proporciona una interfaz unificada y de alto nivel para un conjunto de interfaces en un subsistema. Su objetivo es simplificar el uso de sistemas complejos, ocultando detalles internos y reduciendo la cantidad de dependencias directas que debe conocer el cliente.

En la práctica, una fachada actúa como capa de orquestación: coordina llamadas a servicios, traduce datos y ofrece operaciones de negocio coherentes. Esto facilita el aprendizaje, mejora la mantenibilidad y permite desacoplar módulos.

13.1 Problema que resuelve

Los subsistemas grandes suelen exponer numerosas clases, operaciones y detalles de configuración que el cliente debe manejar con exactitud. Esto genera acoplamiento, curva de aprendizaje alta y fragilidad ante cambios. Facade introduce un punto de entrada que encapsula la complejidad y expone una API orientada a tareas.

El patrón resulta especialmente útil al integrar código legado, bibliotecas de terceros o módulos con demasiados detalles internos. Con una fachada se evita que el cliente conozca cada clase interna y se reduce el número de dependencias.

13.2 Intención y motivación

La intención es proporcionar una interfaz única y simplificada para un subsistema. Se motiva cuando:

  • El subsistema es complejo o se modifica con frecuencia.
  • Se quiere desacacoplar a los clientes de detalles tecnológicos.
  • Se necesita exponer un conjunto de operaciones coherentes siguiendo el lenguaje del dominio.

Una fachada puede ofrecer operaciones de grano grueso que combinan varias llamadas internas, facilitando la reutilización y evitando que los clientes repliquen secuencias de pasos.

13.3 Estructura y participantes

Los participantes principales son:

  • Facade: clase que expone la interfaz simplificada y delega en el subsistema.
  • Subsistemas: clases que implementan el trabajo real; pueden ser numerosos y no conocen la fachada.
  • Clientes: interactúan solo con la fachada para realizar tareas de alto nivel.

El patrón no prohíbe que el cliente acceda directamente al subsistema, pero la fachada actúa como un camino recomendado. También puede utilizar otras fachadas internas para componer funcionalidades.

13.4 Implementación básica en Java

Analicemos un ejemplo en Java donde un sistema de pedidos involucra verificación de inventario, procesamiento de pagos y logística. La fachada GestionPedidosFacade expone una operación de alto nivel realizarPedido.

public class InventarioService {
    public boolean hayStock(String productoId, int cantidad) {
        System.out.println("Verificando stock para " + productoId);
        return true;
    }
}

public class PagoService {
    public void procesarPago(String clienteId, double monto) {
        System.out.println("Procesando pago de " + monto + " para " + clienteId);
    }
}

public class LogisticaService {
    public void despachar(String productoId, int cantidad, String destino) {
        System.out.println("Despachando " + cantidad + " unidades de " + productoId + " hacia " + destino);
    }
}

public class GestionPedidosFacade {
    private final InventarioService inventarioService;
    private final PagoService pagoService;
    private final LogisticaService logisticaService;

    public GestionPedidosFacade(InventarioService inventarioService, PagoService pagoService, LogisticaService logisticaService) {
        this.inventarioService = inventarioService;
        this.pagoService = pagoService;
        this.logisticaService = logisticaService;
    }

    public void realizarPedido(String clienteId, String productoId, int cantidad, String destino, double monto) {
        if (!inventarioService.hayStock(productoId, cantidad)) {
            throw new IllegalStateException("No hay stock disponible");
        }
        pagoService.procesarPago(clienteId, monto);
        logisticaService.despachar(productoId, cantidad, destino);
        System.out.println("Pedido completado para " + clienteId);
    }
}

El cliente interactúa con la fachada en lugar de coordinar manualmente los servicios.

13.5 Ejemplo completo en Java: fachada para un subsistema multimedia

Supongamos un módulo que reproduce medios digitales. Incluye decodificadores, servicios de streaming, cacheo y registros. Una fachada permite reproducir un archivo con una sola llamada, ocultando la configuración interna.

public class DecodificadorAudio {
    public void decodificar(String archivo) {
        System.out.println("Decodificando audio de " + archivo);
    }
}

public class DecodificadorVideo {
    public void decodificar(String archivo) {
        System.out.println("Decodificando video de " + archivo);
    }
}

public class CacheMedios {
    public boolean estaEnCache(String archivo) {
        System.out.println("Verificando cache para " + archivo);
        return false;
    }

    public void almacenar(String archivo) {
        System.out.println("Almacenando " + archivo + " en cache");
    }
}

public class RegistroEventos {
    public void registrar(String mensaje) {
        System.out.println("LOG: " + mensaje);
    }
}

public class ReproductorFacade {
    private final DecodificadorAudio decodificadorAudio = new DecodificadorAudio();
    private final DecodificadorVideo decodificadorVideo = new DecodificadorVideo();
    private final CacheMedios cache = new CacheMedios();
    private final RegistroEventos registroEventos = new RegistroEventos();

    public void reproducir(String archivo) {
        registroEventos.registrar("Reproducción solicitada para " + archivo);
        if (!cache.estaEnCache(archivo)) {
            cache.almacenar(archivo);
            decodificadorAudio.decodificar(archivo);
            decodificadorVideo.decodificar(archivo);
        }
        System.out.println("Reproduciendo " + archivo);
    }
}

El cliente solo necesita conocer la fachada:

public class AplicacionMultimedia {
    public static void main(String[] args) {
        ReproductorFacade reproductor = new ReproductorFacade();
        reproductor.reproducir("pelicula.mp4");
    }
}

13.6 Beneficios y buenas prácticas

  • Simplifica la interfaz del subsistema y reduce la cantidad de dependencias que debe manejar el cliente.
  • Encapsula detalles de implementación, facilitando el cambio de componentes internos sin afectar a los consumidores.
  • Permite definir operaciones de negocio expresivas que representan casos de uso completos.
  • Facilita la adopción progresiva de un sistema complejo ofreciendo puntos de entrada graduales.

Es recomendable colocar las fachadas cerca de la capa de aplicación o infraestructura, documentar su responsabilidad y mantenerlas delgadas para evitar lógica excesiva.

13.7 Riesgos y consideraciones

Una fachada puede volverse un monolito si concentra demasiadas responsabilidades. Se sugiere dividirlas por contexto o caso de uso. Además, si el cliente necesita capacidades avanzadas, la fachada no debe impedir el acceso directo a componentes especializados.

Otro riesgo es que la fachada termine duplicando reglas de dominio o mezclando niveles de abstracción. Mantener una separación clara entre orquestación (fachada) y reglas de negocio ayuda a preservar la coherencia.

13.8 Cuándo elegir Facade

Este patrón es conveniente cuando:

  • Se expone una API hacia clientes externos y se desea proteger los detalles internos.
  • Se integran subsistemas heterogéneos que requieren coordinación.
  • Se necesita una capa anticorrupción que traduzca entre modelos.
  • Se busca mejorar la experiencia de desarrolladores nuevos en el proyecto.

Facade se complementa con otros patrones como Adapter (para traducir interfaces específicas dentro de la fachada) y Mediator (cuando la interacción entre componentes requiere reglas coordinadas).