January 7, 2026 (3mo ago) — last updated March 7, 2026 (1mo ago)

Patrón Adaptador en TypeScript — Guía práctica

Guía práctica del patrón Adaptador en TypeScript: conecta APIs incompatibles, refactoriza código legacy y crea integraciones mantenibles.

← Back to blog
Cover Image for Patrón Adaptador en TypeScript — Guía práctica

El patrón Adaptador permite que componentes con interfaces distintas trabajen juntos sin modificar su código. En esta guía encontrarás ejemplos en TypeScript para integrar APIs incompatibles, refactorizar código legacy y crear integraciones mantenibles.

Patrón Adaptador en TypeScript — Guía práctica

Explora el patrón Adaptador con ejemplos reales en TypeScript. Conecta APIs incompatibles, refactoriza código legacy y crea integraciones mantenibles.

Introducción

El patrón de diseño Adaptador actúa como un intermediario que permite que dos sistemas con interfaces distintas se comuniquen sin modificar su código fuente original. Aplicado correctamente, protege la lógica central de tu aplicación y facilita la modernización incremental de sistemas legacy y la integración de APIs externas1.

Por qué tu base de código necesita el patrón Adaptador

Un diagrama ilustra una API XML legacy conectándose a una aplicación JSON moderna usando un adaptador.

Imagina que intentas enchufar un portátil comprado en Canadá a una toma europea: el hardware funciona, pero las interfaces no coinciden. Lo mismo ocurre en software cuando un servicio devuelve XML y tu frontend espera JSON, o cuando una pasarela de pago expone una API propia y tu aplicación necesita una interfaz estándar.

Hoy rara vez construimos todo desde cero; muchas aplicaciones integran librerías de terceros y sistemas legacy, lo que obliga a afrontar incompatibilidades de interfaz y riesgos de deuda técnica4. El patrón Adaptador te permite encapsular la traducción en una clase dedicada, reduciendo duplicación de lógica, acoplamiento y esfuerzo de mantenimiento2.

El patrón Adaptador convierte la interfaz de una clase en otra interfaz que los clientes esperan, permitiendo que componentes incompatibles trabajen juntos3.

Beneficios clave

  • Centraliza la lógica de traducción en una única clase.
  • Protege la lógica de negocio de detalles de integración.
  • Facilita la migración incremental y la sustitución de dependencias.
  • Mejora la testabilidad mediante mocks del Adaptee.

Cómo funciona en la práctica

Cuatro roles hacen posible el patrón:

  1. Cliente: el código que solicita un servicio.
  2. Target: la interfaz que espera el Cliente.
  3. Adaptee: el componente existente con una interfaz incompatible.
  4. Adaptador: implementa Target y traduce llamadas al Adaptee.

Diagrama dibujado a mano que ilustra el patrón Adaptador.

El Cliente sólo interactúa con Target; el Adaptador traduce en silencio para el Adaptee. Este enfoque respeta el Principio Abierto/Cerrado: para integrar un nuevo servicio basta con escribir un nuevo adaptador sin tocar el código cliente.

Ejemplos reales en TypeScript

A continuación hay dos ejemplos prácticos que reflejan problemas comunes: adaptar una API XML legacy y estandarizar una pasarela de pagos.

Ejemplo 1: Adaptando una API XML legacy a JSON

Escenario: el frontend espera JSON, pero la única fuente disponible devuelve XML. Creamos una interfaz IUserService y un adaptador que la implementa.

// Adaptee: servicio legacy con interfaz incompatible
class LegacyUserService {
  fetchUsersXML(): string {
    return `
      <users>
        <user id="1">
          <name>Alice</name>
          <email>alice@example.com</email>
        </user>
        <user id="2">
          <name>Bob</name>
          <email>bob@example.com</email>
        </user>
      </users>
    `;
  }
}

interface IUser {
  id: number;
  name: string;
  email: string;
}

interface IUserService {
  getUsers(): Promise<IUser[]>;
}

class UserServiceAdapter implements IUserService {
  private adaptee: LegacyUserService;

  constructor(legacyService: LegacyUserService) {
    this.adaptee = legacyService;
  }

  async getUsers(): Promise<IUser[]> {
    const xmlData = this.adaptee.fetchUsersXML();
    // En producción usa un parser robusto (por ejemplo, xml2js).
    console.log("Translating XML to JSON...");
    return [
      { id: 1, name: "Alice", email: "alice@example.com" },
      { id: 2, name: "Bob", email: "bob@example.com" },
    ];
  }
}

Este adaptador permite que el cliente use IUserService sin conocer el XML.

Ejemplo 2: Estandarizando una pasarela de pago

Escenario: tu aplicación usa una interfaz IPaymentProcessor, pero la pasarela PayWizard expone métodos distintos. El adaptador mapea llamadas estándar a la API de PayWizard.

class PayWizard {
  startTransaction(amount: number, cardDetails: string): string {
    console.log(`PayWizard: Initiating transaction for $${amount}.`);
    const transactionId = "pw_" + Math.random().toString(36).substr(2, 9);
    return transactionId;
  }

  verifyPaymentStatus(transactionId: string): boolean {
    console.log(`PayWizard: Verifying status for ${transactionId}.`);
    return true;
  }
}

interface IPaymentProcessor {
  processPayment(amount: number, cardInfo: string): Promise<string>;
  checkStatus(id: string): Promise<boolean>;
}

class PayWizardAdapter implements IPaymentProcessor {
  private payWizard: PayWizard;

  constructor() {
    this.payWizard = new PayWizard();
  }

  async processPayment(amount: number, cardInfo: string): Promise<string> {
    console.log("Adapter: Translating 'processPayment' to 'startTransaction'.");
    return this.payWizard.startTransaction(amount, cardInfo);
  }

  async checkStatus(id: string): Promise<boolean> {
    console.log("Adapter: Translating 'checkStatus' to 'verifyPaymentStatus'.");
    return this.payWizard.verifyPaymentStatus(id);
  }
}

Usar adaptadores mantiene el código de la aplicación consistente entre proveedores y facilita el cambio de proveedor sin tocar la lógica cliente.

Refactorizando código legacy con adaptadores

Diagrama que ilustra un proceso de migración en cuatro pasos usando un adaptador.

Todo proyecto eventualmente se enfrenta a código legacy; el patrón Adaptador permite envolver sistemas antiguos con una interfaz moderna y migrar el código cliente de forma incremental4.

Plan de migración práctico:

  1. Define la interfaz Target que quieres que consuman los clientes.
  2. Crea la clase Adaptador que implemente Target y acepte el Adaptee legacy.
  3. Implementa la lógica de traducción y añade tests unitarios.
  4. Migra el código cliente de forma gradual.

Las herramientas de IA pueden acelerar la generación de boilerplate, pero las decisiones arquitectónicas y los mapeos críticos deben ser revisados por el equipo. Escribe tests de mapeo y añade benchmarks cuando el adaptador esté en rutas críticas.

Elegir entre Adaptador y otros patrones

  • Adaptador: convierte una interfaz en otra.
  • Decorator: añade responsabilidades sin cambiar la interfaz.
  • Proxy: controla acceso, caching o inicialización perezosa.
  • Façade: simplifica un subsistema complejo tras una interfaz única.

Resumen práctico

PatrónIntención principalCuándo usar
AdapterConvertir una interfaz a otraCuando una clase existente debe trabajar con un cliente incompatible.
DecoratorAñadir responsabilidadesCuando quieres extender comportamiento dinámicamente.
ProxyControlar accesoCuando necesitas lazy loading, control de acceso o logging.
FaçadeSimplificar subsistemasCuando quieres un único punto de entrada a comportamiento complejo.

Llevar el patrón Adaptador a tu equipo

Para evitar su uso excesivo, establece normas claras:

  • Documentación: cada adaptador debe incluir un README que describa el Adaptee, la interfaz Target y el mapeo.
  • Pruebas: exige tests unitarios para la lógica de traducción y pruebas de integración con la dependencia real.
  • Rendimiento: mide adaptadores en rutas calientes y añade benchmarks cuando sea necesario.

Automatiza chequeos en CI para garantizar plantillas y pruebas, y publica ejemplos y plantillas en la documentación interna, por ejemplo, en tu guía de modernización de sistemas.

Preguntas rápidas (Q&A)

¿Cuándo usar un adaptador en vez de reescribir un componente?

Usa un adaptador cuando el componente existente funciona pero su interfaz no coincide con tus necesidades, especialmente con sistemas legacy estables o APIs de terceros que no controlas. Reescribe sólo si la dependencia tiene errores críticos o carece de funcionalidades necesarias.

¿Los adaptadores afectan el rendimiento?

Normalmente añaden una sobrecarga muy pequeña —una llamada extra o una conversión de datos— que suele ser insignificante frente a latencia de red o E/S. Para rutas sensibles a la latencia, mide y optimiza el adaptador.

¿Cómo testear adaptadores?

Escribe tests unitarios centrados en los mapeos entre Target y Adaptee, mockeando el Adaptee cuando corresponda. Complementa con tests de integración para validar el comportamiento con la dependencia real.

1.
Refactoring Guru, “Adapter,” https://refactoring.guru/design-patterns/adapter
2.
Clean Code Guy, “Modernizing Legacy Systems,” https://cleancodeguy.com/blog/modernizing-legacy-systems
3.
Wikipedia, “Adapter pattern,” https://en.wikipedia.org/wiki/Adapter_pattern
4.
Stack Overflow, Developer Survey — insights on working with legacy code: https://insights.stackoverflow.com/survey
← Back to blog
🙋🏻‍♂️

La IA escribe código.
Tú lo haces durar.

En la era de la aceleración de la IA, el código limpio no es solo una buena práctica — es la diferencia entre sistemas que escalan y bases de código que colapsan bajo su propio peso.