O que Arquiteto de Frontend faz?
Projete a estrutura mais simples que ainda fará sentido quando a base de código for cinco vezes maior. Prefira padrões bem compreendidos e estabelecidos. Toda camada adicionada deve remover mais complexidade do que introduz.
1. Entenda antes de estruturar
- Leia o aplicativo existente: framework e roteador, modo de renderização (SPA, SSR, RSC, estático), fontes de dados, bibliotecas de estado atuais, abordagem de estilização, testes, ferramentas de build.
- Identifique os fluxos de usuário principais e os dados que cada um necessita. A arquitetura segue os dados.
- Observe as restrições: tamanho da equipe, necessidade de SEO, necessidade offline, necessidade em tempo real, orçamento de desempenho.
2. Componentes: um trabalho cada
Siga a abordagem que a documentação do React chama de "Pensando em React":
- Divida a UI em uma hierarquia; cada componente faz uma coisa. Se um componente precisar de uma descrição longa, divida-o.
- Construa primeiro uma versão estática a partir dos dados.
- Encontre o estado mínimo. Tudo que pode ser derivado de props ou de outro estado é computado, não armazenado.
- Coloque cada pedaço de estado no proprietário comum mais próximo dos componentes que o utilizam.
- Passe callbacks para baixo; os dados fluem em uma direção.
Camadas de componentes:
- Primitivos: Button, Input, Select, Dialog, Tooltip. Acessíveis, estilizados por tokens, sem lógica de negócio. Prefira uma biblioteca headless (Radix, React Aria, Headless UI, Ark) em vez de gerenciamento de foco feito manualmente.
- Padrões: FormField, DataTable, EmptyState, PageHeader. Compõem primitivos.
- Funcionalidades: InvoiceList, BillingSettings. Possuem busca de dados e regras de negócio para um domínio.
- Rotas/páginas: compõem funcionalidades; lidam com parâmetros, layout e limites de loading.
Composição sobre configuração: prefira children/slots a componentes com vinte props booleanas.
3. Estado: classifique-o primeiro
| Tipo | Onde vive |
|---|---|
| Estado do servidor (dados de APIs) | Uma biblioteca de cache: TanStack Query, SWR, RTK Query, ou loaders do framework / componentes de servidor. Nunca copiado para um store global. |
| Estado da URL (filtros, abas, paginação, item selecionado) | A URL (parâmetros de busca, parâmetros de rota). |
| Estado de formulário | A biblioteca de formulários (React Hook Form, Conform, formulários nativos + ações) ou inputs não controlados. |
| Estado local da UI (aberto/fechado, hover) | Estado do componente. |
| Estado compartilhado do cliente (sessão, preferências, rascunhos multi-etapa) | Context para valores de baixa frequência; Zustand/Jotai/Redux apenas quando muitos componentes distantes o atualizam frequentemente. |
Atualizações otimistas para mutações de baixo risco, com reversão em caso de erro. Mutações idempotentes para tudo que um clique duplo poderia repetir.
4. Estrutura de pastas
Organize por funcionalidade, com uma camada compartilhada enxuta:
src/
app/ ou routes/ # roteamento, layouts, limites de loading/erro
features/
billing/
components/
api.ts # queries e mutações para este domínio
hooks.ts
types.ts
index.ts # superfície pública da funcionalidade
components/ui/ # primitivos e padrões (design system)
lib/ # helpers independentes de framework, cliente de API
styles/ # tokens.css, resets globais
Regras: funcionalidades importam de components/ui e lib, nunca dos internos de outra funcionalidade
(vá através do seu index.ts). Sem utils.ts como depósito; nomeie helpers pelo que fazem.
5. Estratégia de estilização
- Tokens como propriedades customizadas CSS são a fonte única da verdade (veja o
tokens.mdda habilidadefrontend-designou a habilidadedesign-system-generator). - Escolha uma abordagem de estilização para a base de código: Tailwind mapeado a tokens, CSS Modules, ou um sistema CSS-in-JS / atômico de zero tempo de execução (vanilla-extract, StyleX, Panda). Evite CSS-in-JS de tempo de execução em aplicações sensíveis a desempenho ou renderizadas no servidor.
- Mantenha a especificidade plana; variantes são props, não substituições por cascata.
6. Orçamentos de desempenho e qualidade
- Defina orçamentos: LCP < 2,5s, INP < 200ms, CLS < 0,1 em um celular de gama média; orçamento de JS por rota.
- Code-split por rota; carregue com lazy loading componentes pesados, abaixo da dobra ou apenas de interação.
- Reserve espaço para conteúdo assíncrono; virtualize listas longas; mova trabalho pesado para fora da thread principal.
- Imagens: formatos modernos, tamanhos explícitos,
srcsetresponsivo, prioridade apenas para a imagem LCP. - Fontes: auto-hospedadas, subconjunto, pré-carregadas com moderação,
font-display: swap. - Acessibilidade é arquitetura: primitivos gerenciam foco e ARIA para que funcionalidades não possam errar. Adicione verificações automatizadas (axe nos testes, eslint-plugin-jsx-a11y) ao CI.
- Limites de erro por rota e por widget independente; uma falha nunca deixa o aplicativo em branco.
7. Saída
Entregue: um breve resumo da arquitetura (pilha, modo de renderização, classificação de estado), a árvore de pastas, a hierarquia de componentes para os fluxos principais, a abordagem de busca de dados e um plano de migração em pequenas etapas entregáveis, se estiver reestruturando um aplicativo existente. Destaque os trade-offs e o que você deliberadamente não adicionou.
Checklist
- Cada pedaço de estado tem um proprietário e um tipo (servidor, URL, formulário, local, compartilhado).
- Dados do servidor são armazenados em cache, não copiados em stores globais.
- Funcionalidades são isoladas atrás de um index público; primitivos não têm lógica de negócio.
- Uma abordagem de estilização, orientada por tokens.
- Code splitting por rota e limites de loading/erro existem.
- Orçamentos de desempenho e verificações de acessibilidade são definidos e automatizados onde possível.