Abby's Digital Cafe

Patrón Estructural - Facade

por Abigail Palmero (Abbytec)

¿Qué es el patrón estructural Facade (Fachada)?

El patrón Facade (también conocido como Fachada) es una forma de organizar tu código para que usar un sistema complejo sea más fácil. En lugar de obligarte a llamar a muchas clases o servicios (y recordar el orden correcto), creas una clase “puerta de entrada” con métodos simples que hacen el trabajo por dentro. Piensa en una fachada de un edificio: desde afuera ves una entrada clara. No necesitas entender cómo está distribuido cada piso para entrar y hacer un trámite. Con Facade, tu código “cliente” (la parte que usa el sistema) habla con una interfaz simple, y la fachada se encarga del resto.

¿Cuándo conviene usar Facade?

Usa Facade cuando notes alguna de estas señales:

  • Para completar una tarea necesitas llamar a 3–10 cosas distintas y te confundes con el orden.
  • Tienes lógica repetida: en varios lugares haces las mismas llamadas a los mismos servicios.
  • Quieres que el resto del proyecto dependa menos de detalles internos (para poder cambiar cosas sin romper todo).
  • Estás integrando una librería o módulo y no quieres “filtrar” su complejidad por todo tu código.

Beneficios (explicados sin humo)

  • Código más simple de leer: el cliente llama 1 método en vez de coordinar muchas piezas.
  • Menos errores de uso: la fachada puede imponer un orden correcto (por ejemplo: validar antes de pagar).
  • Más fácil de cambiar: puedes reemplazar un servicio interno sin tocar todo el proyecto, siempre que la fachada mantenga la misma interfaz.
  • Mejor para principiantes: reduce la cantidad de conceptos que necesitas tener en la cabeza al mismo tiempo.

Cómo implementarlo paso a paso

  1. Identifica una tarea frecuente (ej: “hacer una compra”, “crear un reporte”, “registrar un usuario”).
  2. Lista qué clases/servicios participan y en qué orden se suelen llamar.
  3. Crea una clase Facade con un método que represente esa tarea (ej: checkout(), generarReporte(), registrarUsuario()).
  4. Dentro del método, coordina las llamadas internas y maneja errores comunes.
  5. Haz que el resto del código use la Facade en lugar de acceder directamente a las piezas internas.

Ejemplo 1 (TypeScript): Checkout de una tienda

Imagina un e-commerce. Para finalizar una compra necesitas: validar stock, calcular el total, cobrar, y emitir una confirmación. Si cada pantalla repite esas llamadas, tu app se vuelve frágil. Una Facade de Checkout lo deja en un solo método.

[typescript]
type Item = { sku: string; qty: number; price: number }; type PaymentResult = { ok: boolean; paymentId?: string; message?: string }; type OrderConfirmation = { orderId: string; paymentId: string; total: number; }; class InventoryService { hasStock(items: Item[]): boolean { // En la vida real consultarías una base de datos o API. return items.every((i) => i.qty > 0); } } class PricingService { calculateTotal(items: Item[]): number { return items.reduce((sum, i) => sum + i.price * i.qty, 0); } } class PaymentGateway { charge(amount: number, cardToken: string): PaymentResult { if (!cardToken) return { ok: false, message: "Tarjeta inválida" }; // Simulación return { ok: true, paymentId: "pay_" + Math.random().toString(16).slice(2) }; } } class OrderService { createOrder(total: number, paymentId: string): OrderConfirmation { return { orderId: "ord_" + Math.random().toString(16).slice(2), paymentId, total }; } } // === FACADE === class CheckoutFacade { constructor( private inventory: InventoryService, private pricing: PricingService, private payment: PaymentGateway, private orders: OrderService ) {} checkout(items: Item[], cardToken: string): OrderConfirmation { if (!this.inventory.hasStock(items)) { throw new Error("No hay stock suficiente para completar la compra"); } const total = this.pricing.calculateTotal(items); const result = this.payment.charge(total, cardToken); if (!result.ok || !result.paymentId) { throw new Error(result.message ?? "Pago rechazado"); } return this.orders.createOrder(total, result.paymentId); } } // === USO === const facade = new CheckoutFacade( new InventoryService(), new PricingService(), new PaymentGateway(), new OrderService() ); const confirmation = facade.checkout( [ { sku: "BOOK-001", qty: 1, price: 25 }, { sku: "PEN-002", qty: 2, price: 3 } ], "tok_visa_demo" ); console.log("Compra confirmada:", confirmation);

Observa el cambio mental: el código cliente solo dice checkout(...). La Facade se ocupa de coordinar lo demás. Si mañana cambias el proveedor de pagos, idealmente solo ajustas el interior de la fachada.


Ejemplo 2 (Java): Alta de usuario con bienvenida

Ahora en Java: al registrar un usuario podrías necesitar validar datos, guardar en base de datos, asignar un rol por defecto y enviar un correo de bienvenida. En lugar de repartir esa coordinación por la app, creas una fachada.

[java]
class User { public final String email; public final String name; public User(String email, String name) { this.email = email; this.name = name; } } class ValidationService { public void validateNewUser(User user) { if (user.email == null || !user.email.contains("@")) { throw new IllegalArgumentException("Email inválido"); } if (user.name == null || user.name.trim().isEmpty()) { throw new IllegalArgumentException("Nombre requerido"); } } } class UserRepository { public void save(User user) { // Simulación: en la vida real guardarías en una DB. System.out.println("Guardando usuario: " + user.email); } } class RoleService { public void assignDefaultRole(String email) { System.out.println("Asignando rol DEFAULT a: " + email); } } class EmailService { public void sendWelcomeEmail(String email) { System.out.println("Enviando email de bienvenida a: " + email); } } // === FACADE === class UserOnboardingFacade { private final ValidationService validation; private final UserRepository repo; private final RoleService roles; private final EmailService email; public UserOnboardingFacade( ValidationService validation, UserRepository repo, RoleService roles, EmailService email ) { this.validation = validation; this.repo = repo; this.roles = roles; this.email = email; } public void register(User user) { validation.validateNewUser(user); repo.save(user); roles.assignDefaultRole(user.email); email.sendWelcomeEmail(user.email); } } public class Main { public static void main(String[] args) { UserOnboardingFacade facade = new UserOnboardingFacade( new ValidationService(), new UserRepository(), new RoleService(), new EmailService() ); facade.register(new User("[email protected]", "Ana")); } }

Este enfoque hace que el “caso de uso” sea fácil de encontrar: si alguien pregunta “¿cómo se registra un usuario?”, la respuesta está en un solo lugar: UserOnboardingFacade.register(...).

Preguntas frecuentes (con respuestas claras)

¿Facade es lo mismo que una API?

Se parecen en el objetivo: simplificar el uso. Pero una API suele ser el “contrato” hacia afuera (por ejemplo, endpoints HTTP), mientras que una Facade normalmente vive dentro del código para simplificar un módulo o subsistema. Puedes tener una API que, por dentro, use una Facade.

¿Facade reemplaza a las clases internas?

No. Las clases internas siguen existiendo y hacen el trabajo real. La Facade no elimina esas piezas: las organiza y ofrece un punto de entrada más cómodo. Si necesitas algo muy específico, podrías seguir usando una clase interna, pero intenta que lo común pase por la fachada.

¿Cómo evito que la Facade se vuelva enorme?

Mantén el foco: una Facade debería representar acciones completas (casos de uso), no ser un “bolsón” con métodos sueltos. Si empieza a crecer demasiado, crea más de una fachada, cada una para un área (por ejemplo, CheckoutFacade y RefundFacade), o agrupa por flujo (OnboardingFacade, AccountSettingsFacade).

Resumen para llevar

  • Facade te ayuda a usar sistemas complejos con una interfaz simple.
  • Es ideal cuando hay muchas llamadas repetidas o un orden fácil de romper.
  • Mejora legibilidad y mantenimiento, especialmente en equipos o proyectos en crecimiento.
  • Empieza por el flujo principal, valida errores comunes y evita que la fachada crezca sin control.

Comentarios