December 19, 2025 (4mo ago) — last updated February 10, 2026 (2mo ago)

Patrón Adapter: Ejemplos en TypeScript, React y Node.js

Aprende cómo el patrón Adapter conecta interfaces incompatibles con ejemplos en TypeScript, React y Node.js para modernizar integraciones y código legado.

← Back to blog
Cover Image for Patrón Adapter: Ejemplos en TypeScript, React y Node.js

Aprende cómo el patrón Adapter conecta interfaces incompatibles con ejemplos en TypeScript, React y Node.js para modernizar integraciones y código legado.

Patrón Adapter: Ejemplos en TypeScript, React & Node.js

Resumen: Aprende cómo el patrón Adapter conecta interfaces incompatibles en TypeScript, React y Node.js con ejemplos prácticos y del mundo real.

Introducción

¿Alguna vez has tenido una biblioteca o un módulo legado perfectamente funcional que simplemente no encaja con el resto de tu sistema? Es como intentar enchufar un adaptador europeo en una toma norteamericana: ambos funcionan, pero sus interfaces no coinciden. El patrón Adapter lo soluciona traduciendo una interfaz a otra para que puedas reutilizar código existente sin cambiarlo.

Esta guía explica el patrón, muestra ejemplos en TypeScript y React, y demuestra un adaptador en Node.js que moderniza módulos basados en callbacks. Se centra en técnicas prácticas que puedes aplicar de inmediato para limpiar integraciones y mejorar la capacidad de prueba.

Por qué importa el patrón Adapter

El patrón Adapter es un patrón estructural que envuelve un objeto incompatible y expone la interfaz que tu código espera. Fue documentado por primera vez por la Gang of Four en 19941. Los adapters son esenciales para integrar APIs de terceros, modernizar código legado y unificar fuentes de datos dispares.

Escenarios comunes donde los adapters ayudan:

  • Integrar APIs de terceros que devuelven formas de datos diferentes.
  • Modernizar APIs legadas basadas en callbacks para que funcionen con async/await.
  • Unificar múltiples fuentes de datos en una sola interfaz para componentes de UI.

Usar un adapter mantiene la lógica de negocio limpia y desacoplada de sistemas externos.

Patrón Adapter en resumen

ConceptoDescripción
TipoEstructural
Intención principalPermitir que objetos con interfaces incompatibles trabajen juntos
Idea centralEnvolver al adaptee para exponer la interfaz target
Problema clave resueltoReutilizar clases existentes sin cambiar su código fuente
Casos de uso comunesLibrerías de terceros, código legado, múltiples fuentes de datos

Estructura y roles

El patrón tiene cuatro roles:

  1. El Cliente — el código que necesita una interfaz específica.
  2. La Interfaz Target — el contrato que el Cliente espera.
  3. El Adaptee — la clase o módulo incompatible con la funcionalidad necesaria.
  4. El Adapter — implementa el Target y delega al Adaptee, traduciendo llamadas según sea necesario.

Dos estilos comunes de adapter:

  • Adapter por objeto (composición): el Adapter contiene una instancia del Adaptee. Esta es la aproximación más flexible.
  • Adapter por clase (herencia): el Adapter hereda del Adaptee e implementa el Target. Esto requiere herencia múltiple y es menos común en JavaScript y TypeScript modernos.

Ejemplo práctico: TypeScript + React

Imagina un panel que recibe perfiles de usuario de dos servicios con formas de respuesta distintas. Sin un adapter, los componentes se llenan de lógica condicional.

Formas de API incompatibles

// Data from UserServiceA
interface UserA {
  userId: number;
  fullName: string;
  emailAddress: string;
}

// Data from UserServiceB
interface UserB {
  id: string;
  name: string;
  contact: {
    email: string;
  };
}

Interfaz target que nuestra app espera

interface UnifiedUser {
  id: string;
  name: string;
  email: string;
}

Adapters en TypeScript

// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
  return {
    id: userA.userId.toString(),
    name: userA.fullName,
    email: userA.emailAddress,
  };
}

// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
  return {
    id: userB.id,
    name: userB.name,
    email: userB.contact.email,
  };
}

Centralizar las transformaciones mantiene los componentes limpios y resilientes. Si una API renombra un campo, solo cambia el adapter.

Componente React que consume datos unificados

interface UserProfileProps {
  user: UnifiedUser;
}

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

Este componente depende de una única forma predecible, lo que facilita las pruebas y la reutilización.

Ejemplo: Modernizando un módulo de Node.js basado en callbacks

Los módulos legados suelen usar callbacks con el patrón error-first. En lugar de modificar un módulo estable, construye un adapter que exponga una API basada en Promises.

Adaptee legado (no modificar)

// legacyFileProcessor.js
const fs = require('fs');

class LegacyFileProcessor {
  processFile(filePath, callback) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return callback(err, null);
      }
      const processedContent = data.toUpperCase();
      callback(null, processedContent);
    });
  }
}

module.exports = LegacyFileProcessor;

Adapter que devuelve una Promise

// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');

class FileProcessorAdapter {
  constructor() {
    this.legacyProcessor = new LegacyFileProcessor();
  }

  processFile(filePath) {
    return new Promise((resolve, reject) => {
      this.legacyProcessor.processFile(filePath, (err, data) => {
        if (err) return reject(err);
        resolve(data);
      });
    });
  }
}

module.exports = FileProcessorAdapter;

Esto refleja el comportamiento de util.promisify de Node, pero mantiene la lógica de adaptación explícita y verificable mediante pruebas3.

Uso del adapter en el código de la aplicación

const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();

async function handleFileProcessing() {
  try {
    console.log('Processing file with modern async/await...');
    const content = await fileProcessor.processFile('my-file.txt');
    console.log('Processed Content:', content);
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

handleFileProcessing();

Esto mantiene el código legado intacto mientras ofrece al resto de tu base de código una interfaz moderna.

Cuándo usar un Adapter

Usa un adapter cuando dos componentes no pueden comunicarse directamente porque sus interfaces difieren. Escenarios típicos:

  • Integrar una API de terceros cuyos inputs o outputs no coinciden con tus modelos.
  • Envolver APIs legadas basadas en callbacks para que funcionen con async/await.
  • Soportar múltiples fuentes de datos con diferentes formatos creando un adapter por fuente.

Cuándo no usar un adapter:

  • Si controlas ambos sistemas y un pequeño refactor resolverá la discrepancia, prefiere el refactor directo.
  • Si tu objetivo es simplificar un subsistema complejo, considera un Facade en su lugar. Un Facade ofrece un punto de entrada simplificado y de alto nivel; un Adapter se centra únicamente en la compatibilidad.

Lista rápida de decisión

Situación¿Usar Adapter?Por qué
Necesitas usar una librería de terceros con API incompatibleNo puedes cambiar la librería, así que adáptate a ella
Controlas ambos lados y el cambio es pequeñoNoRefactoriza directamente para evitar indirección extra
Necesitas una interfaz de alto nivel simplificada para un sistema complejoNoFacade es una mejor opción
Migrar sistemas legados de forma incrementalEnvuelve componentes antiguos para que coincidan con nuevas interfaces
Múltiples fuentes de datos con estructuras diferentesLos adapters las unifican en una sola forma

Pruebas y rendimiento

Los adapters mejoran la capacidad de prueba al desacoplar la lógica central de sistemas externos. Puedes simular la interfaz de un adapter para probar componentes en aislamiento, y probar los adapters por separado para verificar la lógica de traducción.

La sobrecarga de rendimiento de un adapter es mínima —típicamente una llamada de función extra— y es insignificante comparada con E/S de red o consultas a bases de datos. Para la mayoría de aplicaciones web, los beneficios de mantenimiento y desacoplamiento superan con creces el coste ínfimo. JavaScript sigue siendo el lenguaje más usado en la encuesta de desarrolladores de Stack Overflow, lo que subraya con qué frecuencia los desarrolladores enfrentan trabajo de integración que los adapters resuelven4.

Preguntas frecuentes

P: ¿Qué problema resuelve el patrón Adapter?

R: Resuelve incompatibilidades de interfaz traduciendo llamadas de un cliente a llamadas que el adaptee entiende para que puedas reutilizar código sin cambiarlo.

P: ¿Cómo ayuda un Adapter con código legado?

R: Un Adapter envuelve módulos legados y expone una interfaz moderna, permitiéndote integrar código antiguo y estable en aplicaciones nuevas sin reescrituras arriesgadas.

P: ¿Cuándo debo elegir un Adapter sobre otros patrones?

R: Elige un Adapter cuando necesites compatibilidad entre dos interfaces que no coinciden. Si quieres simplificar todo un subsistema, usa un Facade en su lugar.

Lecturas adicionales y enlaces internos


En Clean Code Guy ayudamos a equipos a implementar patrones de diseño prácticos que convierten bases de código frágiles y complejas en activos resilientes, comprobables y agradables de trabajar. Si estás lidiando con un sistema legado o integraciones complicadas, nuestras Clean Code Audits pueden darte una hoja de ruta clara y accionable hacia una arquitectura más sana. Aprende cómo podemos ayudarte a lanzar mejor código, más rápido.

1.
Erich Gamma, Richard Helm, Ralph Johnson, y John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns_(book)](https://en.wikipedia.org/wiki/Design_Patterns_(book))
2.
Resumen y ejemplos del patrón Adapter: https://www.geeksforgeeks.org/adapter-pattern/
3.
Documentación de Node.js sobre util.promisify, un enfoque común para convertir callbacks en Promises: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
Stack Overflow Developer Survey 2023, que muestra la prevalencia de JavaScript y tecnologías web que comúnmente requieren trabajo de integración: https://survey.stackoverflow.co/2023/
← 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.