Carregando agora

Design Patterns no React: Entendendo Adapter, Factory e Gateway

Ilustração conceitual sobre design patterns no React com elementos visuais de Adapter, Factory e Gateway.

Fala, dev! Hoje o papo é reto: design patterns no React. Se você já se pegou socando useEffect com requisição HTTP dentro de componente e achou que tava arrasando… sinto muito, mas você caiu na pegadinha do júnior entusiasmado. Bora consertar isso e aprender a usar padrões de design como gente grande.

Antes de tudo, calma. Não precisa pegar seu livro do Gang of Four (ou abrir a Wikipedia). Aqui a ideia é mostrar como esses padrões se aplicam na prática com React, usando humor, exemplos e uns puxões de orelha leves. Bora?


O que são Design Patterns?

Design patterns são soluções padrão para problemas comuns no desenvolvimento de software. A ideia é você não reinventar a roda toda vez que precisa fazer algo complexo. Tá aí o segredo: reutilizar o que já funciona.

Só que tem um problema: a maioria desses padrões foi feita pensando em orientação a objetos. E o React, com seu jeitinho funcional e hooks, muitas vezes pede adaptações. Por isso, vamos ver como encaixar os padrões Adapter, Factory e Gateway no React sem criar um monstro que nem você vai querer mexer depois.


Por que usar Design Patterns no React?

Se você tá pensando: “Pra que complicar? Meu useEffect já resolve tudo”, eu tenho duas palavras pra você: legibilidade e manutenção.

Imagina daqui a 3 meses, quando seu tech lead vier te perguntar por que aquela requisição HTTP tá travando todo o app. Ou pior, quando você mesmo olhar o código e não entender que droga tava tentando fazer. Design patterns existem pra resolver isso.


Adapter: Adaptando Interfaces no React

O Adapter é basicamente um tradutor. Ele pega algo que não encaixa direto no que você precisa e transforma para que funcione sem quebrar tudo.

No React, um caso clássico é quando você tem que integrar várias bibliotecas de requisições HTTP (como Axios e Fetch). Se você não usar um Adapter, vai acabar amarrado em uma dessas libs pra sempre.

Exemplo prático de Adapter

Aqui, criamos um HttpClient genérico que pode ser implementado tanto com Axios quanto com Fetch. O segredo é criar uma interface comum para que seu componente nunca precise saber qual biblioteca está sendo usada.

type HttpRequest = {
  url: string;
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
  body?: any;
  headers?: Record<string, string>;
};

interface HttpClient {
  request(data: HttpRequest): Promise<any>;
}

class AxiosHttpClientAdapter implements HttpClient {
  async request(data: HttpRequest): Promise<any> {
    const response = await axios({
      url: data.url,
      method: data.method,
      data: data.body,
      headers: data.headers,
    });
    return response.data;
  }
}

Agora seu componente pode usar um HttpClient sem nunca saber qual implementação está por trás:

const UserList = ({ httpClient }: { httpClient: HttpClient }) => {
  useEffect(() => {
    const fetchData = async () => {
      const users = await httpClient.request({ url: '/users', method: 'GET' });
      console.log(users);
    };
    fetchData();
  }, [httpClient]);

  return <div>Usuários carregados!</div>;
};

Viu como fica bonito? E se amanhã você quiser trocar o Axios pelo Fetch, só precisa implementar um novo Adapter.


Factory: Fabricando Dependências

Se o Adapter é o tradutor, o Factory é o chefão que cria tudo que o Adapter precisa. Ele centraliza a criação de instâncias, o que facilita a troca e gerenciamento de dependências.

Exemplo prático de Factory

Aqui temos um Factory que decide qual implementação de HttpClient será usada:

class HttpClientFactory {
  static create(): HttpClient {
    return new AxiosHttpClientAdapter();
  }
}

Agora, toda vez que você precisar de um HttpClient, é só chamar o Factory:

const App = () => {
  const httpClient = HttpClientFactory.create();
  return <UserList httpClient={httpClient} />;
};

Se amanhã você quiser trocar o Axios por Fetch? Muda só no Factory. Sem dor de cabeça.


Gateway: Encapsulando o Acesso a Sistemas Externos

O Gateway é o guarda de segurança. Ele encapsula o acesso a sistemas externos, como APIs, garantindo que tudo passe por um lugar só. Isso é ótimo pra organizar o código e evitar bagunça.

Exemplo prático de Gateway

Aqui criamos um Gateway para a API de usuários:

interface UserGateway {
  loadAll(): Promise<any[]>;
}

class ApiUserGateway implements UserGateway {
  private httpClient: HttpClient;

  constructor(httpClient: HttpClient) {
    this.httpClient = httpClient;
  }

  async loadAll(): Promise<any[]> {
    return this.httpClient.request({ url: '/users', method: 'GET' });
  }
}

E no componente:

const UserList = ({ userGateway }: { userGateway: UserGateway }) => {
  useEffect(() => {
    const fetchData = async () => {
      const users = await userGateway.loadAll();
      console.log(users);
    };
    fetchData();
  }, [userGateway]);

  return <div>Usuários carregados!</div>;
};

Agora, se a API mudar ou você precisar de uma nova validação, é só mexer no Gateway.


Por que isso tudo é útil?

  1. Flexibilidade: Mude de biblioteca ou API sem reescrever tudo.
  2. Testabilidade: Fica fácil mockar dependências em testes.
  3. Manutenção: Seu código vira um lego, onde cada peça tem seu lugar.

Erros comuns ao implementar Design Patterns no React

  1. Overengineering: Não transforme seu app em um Frankenstein de padrões. Use só o que resolve o problema.
  2. Não testar: Cada padrão precisa ser testado. Se você não fizer isso, vai acabar criando bugs mais difíceis de achar.
  3. Falta de centralização: Sempre centralize lógica repetida. Se você não fizer isso, os padrões perdem o sentido.

Conclusão: Design Patterns no React na prática

Design patterns no React não são só papo de livro. Eles resolvem problemas reais, como dependências mal gerenciadas e código difícil de testar. Aprendemos como usar Adapter, Factory e Gateway para criar componentes mais flexíveis, legíveis e fáceis de manter.

Agora é sua vez: pega aquele projeto onde tudo tá jogado no useEffect e começa a aplicar essas ideias. Só cuidado pra não virar o dev padrãozão que enfia todos os padrões em todo lugar. Equilíbrio é tudo.

Se curtiu o artigo, compartilha com aquele amigo que adora um código legado horrível. E se ficou com dúvida, manda nos comentários. Quem sabe não vira o tema do próximo conteúdo?


TL;DR para os preguiçosos:

  • Adapter: Traduz interfaces incompatíveis (Ex.: Axios vs Fetch).
  • Factory: Centraliza a criação de dependências.
  • Gateway: Encapsula acesso a sistemas externos.

Aplicar design patterns no React é como colocar ordem no caos. Então bora organizar esse código aí e deixar seu futuro “eu” orgulhoso!

Publicar comentário

O que temos aqui?