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

Padrão Adapter: Exemplos em TypeScript, React e Node.js

Aprenda como o padrão Adapter conecta interfaces incompatíveis com exemplos em TypeScript, React e Node.js para modernizar integrações e código legado.

← Back to blog
Cover Image for Padrão Adapter: Exemplos em TypeScript, React e Node.js

Aprenda como o padrão Adapter conecta interfaces incompatíveis com exemplos em TypeScript, React e Node.js para modernizar integrações e código legado.

Adapter Pattern: TypeScript, React & Node.js Examples

Resumo: Aprenda como o padrão Adapter conecta interfaces incompatíveis em TypeScript, React e Node.js com exemplos práticos do mundo real.

Introdução

Já teve uma biblioteca perfeitamente boa ou um módulo legado que simplesmente não se encaixa no resto do seu sistema? É como tentar encaixar um adaptador europeu em uma tomada norte-americana — ambos funcionam, mas suas interfaces não combinam. O padrão Adapter resolve isso traduzindo uma interface para outra, permitindo que você reutilize código existente sem alterá‑lo.

Este guia explica o padrão, mostra exemplos em TypeScript e React e demonstra um adapter em Node.js que moderniza módulos baseados em callbacks. Ele foca em técnicas práticas que você pode aplicar imediatamente para limpar integrações e melhorar a testabilidade.

Por que o Padrão Adapter é Importante

O padrão Adapter é um padrão estrutural que envolve um objeto incompatível e expõe a interface que seu código espera. Foi documentado pela primeira vez pelo Gang of Four em 19941. Adapters são essenciais para integrar APIs de terceiros, modernizar código legado e unificar fontes de dados díspares.

Cenários comuns em que adapters ajudam:

  • Integrar APIs de terceiros que retornam formatos de dados diferentes.
  • Modernizar APIs legadas baseadas em callbacks para funcionarem com async/await.
  • Unificar múltiplas fontes de dados em uma única interface para componentes de UI.

Usar um adapter mantém a lógica de negócio limpa e desacoplada de sistemas externos.

Padrão Adapter em Resumo

ConceptDescription
TypeStructural
Primary intentAllow objects with incompatible interfaces to work together
Core ideaWrap the adaptee to expose the target interface
Key problem solvedReuse existing classes without changing their source code
Common use casesThird-party libraries, legacy code, multiple data sources

Estrutura e Papéis

O padrão tem quatro papéis:

  1. O Cliente — o código que precisa de uma interface específica.
  2. A Interface Alvo — o contrato que o Cliente espera.
  3. O Adaptee — a classe ou módulo incompatível com a funcionalidade necessária.
  4. O Adapter — implementa a Interface Alvo e delega para o Adaptee, traduzindo chamadas conforme necessário.

Dois estilos comuns de adapter:

  • Adapter por objeto (composição): o Adapter mantém uma instância do Adaptee. Esta é a abordagem mais flexível.
  • Adapter por classe (herança): o Adapter herda do Adaptee e implementa a Interface Alvo. Isso requer herança múltipla e é menos comum em JavaScript e TypeScript modernos.

Exemplo Prático: TypeScript + React

Imagine um painel que recebe perfis de usuário de dois serviços com formatos de resposta diferentes. Sem um adapter, os componentes ficam cheios de lógica condicional.

Formatos de API incompatíveis

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

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

Interface alvo que nossa aplicação espera

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

Adapters em 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 transformações mantém os componentes limpos e resilientes. Se uma API renomear um campo, apenas o adapter muda.

Componente React que consome dados 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 uma única forma previsível, o que facilita testes e reutilização.

Exemplo: Modernizando um Módulo Node.js Baseado em Callback

Módulos legados frequentemente usam callbacks com erro como primeiro parâmetro. Em vez de modificar um módulo estável, construa um adapter que exponha uma API baseada em Promise.

Adaptee legado (não 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 retorna uma 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;

Isto espelha o comportamento do util.promisify do Node, mas mantém a lógica de adaptação explícita e testável3.

Usando o adapter no código da aplicação

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();

Isso mantém o código legado intocado enquanto oferece ao restante da base de código uma interface moderna.

Quando Usar um Adapter

Use um adapter quando dois componentes não conseguem se comunicar diretamente porque suas interfaces diferem. Cenários típicos:

  • Integrar uma API de terceiros cujas entradas ou saídas não correspondem aos seus modelos.
  • Envolver APIs legadas baseadas em callbacks para que funcionem com async/await.
  • Suportar múltiplas fontes de dados com formatos diferentes criando um adapter por fonte.

Quando não usar um adapter:

  • Se você controla ambos os sistemas e um pequeno refactor resolveria a incompatibilidade, prefira refatorar diretamente.
  • Se seu objetivo é simplificar um subsistema complexo, considere um Facade em vez disso. Um Facade oferece um ponto de entrada de alto nível simplificado; um Adapter foca apenas na compatibilidade.

Lista rápida de decisão

SituationUse Adapter?Why
Need to use a third-party library with incompatible APIYesYou can’t change the library, so adapt to it
Control both sides and change is smallNoRefactor directly to avoid extra indirection
Need a simplified high-level interface to a complex systemNoFacade is a better fit
Migrating legacy systems incrementallyYesWrap old components to match new interfaces
Multiple differently structured data sourcesYesAdapters unify them into one shape

Testes e Performance

Adapters melhoram a testabilidade ao desacoplar a lógica central de sistemas externos. Você pode mockar a interface do adapter para testar componentes isoladamente, e testar adapters separadamente para verificar a lógica de tradução.

A sobrecarga de performance de um adapter é mínima — tipicamente uma chamada de função extra — e é insignificante em comparação com operações de rede ou consultas a banco de dados. Para a maioria das aplicações web, os benefícios de manutenção e desacoplamento superam amplamente esse pequeno custo. JavaScript continua sendo a linguagem mais usada na Stack Overflow Developer Survey, o que destaca com que frequência desenvolvedores enfrentam trabalho de integração que adapters resolvem4.

Perguntas Frequentes

P: Qual problema o padrão Adapter resolve?

R: Ele resolve incompatibilidades de interface traduzindo chamadas de um cliente em chamadas que o adaptee entende, permitindo reutilizar código sem alterá‑lo.

P: Como um Adapter ajuda com código legado?

R: Um Adapter envolve módulos legados e expõe uma interface moderna, permitindo integrar código antigo e estável em novas aplicações sem refatorações arriscadas.

P: Quando devo escolher um Adapter em vez de outros padrões?

R: Escolha um Adapter quando precisar compatibilizar duas interfaces incompatíveis. Se você quer simplificar todo um subsistema, use um Facade em vez disso.


At Clean Code Guy, we help teams implement practical design patterns that turn brittle, complex codebases into assets that are resilient, testable, and a pleasure to work on. If you’re wrestling with a legacy system or tricky integrations, our Clean Code Audits can give you a clear, actionable roadmap to a healthier architecture. Learn how we can help you ship better code, faster.

1.
Erich Gamma, Richard Helm, Ralph Johnson, and 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.
Adapter pattern overview and examples: https://www.geeksforgeeks.org/adapter-pattern/
3.
Node.js documentation for util.promisify, a common approach to convert callbacks to Promises: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
Stack Overflow Developer Survey 2023, showing the prevalence of JavaScript and web technologies that commonly require integration work: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

IA escreve código.
Você faz durar.

Na era da aceleração da IA, código limpo não é apenas uma boa prática — é a diferença entre sistemas que escalam e bases de código que entram em colapso sob seu próprio peso.