React é uma biblioteca de Views, não uma arquitetura
Tem um componente no seu codebase que começou inocente. Era só uma lista de produtos. Hoje ele busca dados de dois endpoints, calcula o total do carrinho, aplica a regra de um cupom, decide se o botão de comprar fica desabilitado e ainda escolhe qual mensagem de erro mostrar. Ninguém planejou isso. Aconteceu um pull request razoável de cada vez, cada um com uma justificativa boa no momento. Quando você percebe, abrir aquele arquivo dá um friozinho na espinha, e qualquer mudança vem com medo de quebrar três coisas que pareciam não ter relação nenhuma.
Esse componente é o sintoma. A causa é mais antiga, e quase sempre a mesma: a gente deixou a biblioteca de UI virar o lugar onde a business logic mora.
Antes: o componente faz tudo. Depois: a UI só renderiza e delega para um módulo de domínio sem framework.
O que React (e Vue) realmente entregam
React e Vue são bibliotecas para construir Views. Elas são excelentes nisso, e é bom lembrar exatamente do que elas te dão: renderização de componentes, gerenciamento de estado local, tratamento de interação do usuário e controle do ciclo de vida da UI. É uma lista poderosa, e dá pra construir produtos inteiros em cima dela.
O problema aparece no que não está na lista. React e Vue não te dão padrões de transformação de dados, não resolvem cross-cutting concerns como autenticação e logging, não modelam o seu domínio e não cuidam de acesso a dados. Isso não é crítica nenhuma. Um pincel não tem a obrigação de compor o quadro. A questão é que o framework deixa você colocar tudo isso dentro de um componente, e por um tempo até parece produtivo. A conta chega depois.
Os cinco sintomas
Quando a business logic vaza para componentes e hooks, os mesmos cinco sintomas aparecem em todo codebase, independente da stack. A lógica de negócio fica espalhada, sem um lugar único pra procurar uma regra. A manutenção fica mais cara conforme o app cresce, porque toda mudança encosta na UI. Testar exige montar um componente mesmo quando você só quer verificar um cálculo. Trocar de framework significa reescrever as regras de negócio, porque elas nunca foram separadas do framework. E features novas vivem quebrando features antigas, já que lógica sem relação nenhuma divide o mesmo arquivo.
Se você trabalhou em qualquer frontend com mais de dois anos de vida, reconhece os cinco. Não é sinal de time ruim. É o resultado padrão de tratar uma biblioteca de View como se fosse uma arquitetura.
O que é uma arquitetura “boa”, na prática
É fácil falar de arquitetura no abstrato. É mais útil defini-la pelo que ela te permite fazer. A sua arquitetura está funcionando quando você consegue testar a business logic sem renderizar um único componente, quando consegue substituir React por Vue (ou por um CLI) sem reescrever a lógica de domínio, quando times diferentes trabalham em partes diferentes sem se atropelar, quando uma mudança na fonte de dados (de REST para GraphQL, por exemplo) não toca nas regras de negócio, e quando alguém novo no time entende o código e contribui rápido.
Repare que nenhum desses critérios cita um framework, um nome de pasta ou uma biblioteca. Todos falam de uma coisa só: independência. Da UI em relação à lógica, da lógica em relação à fonte de dados, e dos times entre si.
O CLI test
Existe um teste rápido que captura quase toda essa lista. Pergunte a si mesmo: eu conseguiria rodar essa feature a partir de uma linha de comando, sem UI nenhuma, reaproveitando exatamente a mesma lógica?
Se a resposta é sim, suas regras vivem num lugar que o framework não enxerga, que é exatamente onde elas deveriam estar. Se a resposta é não, porque a lógica só existe dentro de um componente ou de um hook, então sua UI e seu domínio estão fundidos, e todos os custos da seção anterior já estão correndo. Não se trata de construir um CLI de verdade. É um experimento mental que expõe onde a sua lógica realmente está.
Eu uso isso como pergunta de code review. Quando bate a dúvida sobre onde um trecho deveria morar, o CLI test costuma responder antes de qualquer discussão sobre camadas.
Como sair disso sem cair no exagero
A saída não é uma biblioteca nova, é um hábito: manter a business logic em módulos agnósticos de framework, funções e classes que não importam nada de React ou Vue, e deixar a camada de View cuidando só de apresentação. Na prática, isso significa separar responsabilidades. A apresentação lida com o que o usuário vê. O domínio guarda as regras (esse produto pode ir pro carrinho? qual o total depois do cupom?). O acesso a dados sabe se você fala com REST ou GraphQL e traduz a resposta crua em algo que o domínio entende.
Num contexto de e-commerce em Vue/Nuxt, que é onde eu passo meus dias, o ponto de pressão é sempre o carrinho e o checkout. Regras de preço, validação de cupom e checagem de disponibilidade tendem a vazar pra dentro de componentes e composables porque, sob prazo, é o caminho mais curto. Puxar essas regras pra módulos isolados paga duas vezes: a mesma lógica passa a servir uma página Nuxt, um script ou uma futura tela nativa, e ela vira testável sem subir a UI inteira. Se você usa Vue, o post Composable Abstraction Layer: o pattern que faltava entre Pinia e seus componentes mostra exatamente como esse padrão se parece na prática com Pinia.
Vale o aviso na direção contrária, porque o exagero é um modo de falha tão real quanto a bagunça. Não é pra encher o projeto de services e repositories porque um livro mandou. Para um MVP, uma tela de CRUD simples ou algo com prazo de duas semanas, colocar a lógica onde for conveniente costuma ser a decisão certa. A habilidade está em dar a quantidade de estrutura que o problema pede, e revisitar essa decisão conforme o produto cresce, em vez de escolher uma vez e defender pra sempre.
O que eu faria hoje
Se eu pudesse plantar uma única ideia num time de frontend, não seria “sempre adicione camadas”. Seria o CLI test como hábito. A maioria dos times não precisa de uma arquitetura de livro; precisa de um jeito barato e honesto de checar se a lógica de domínio está presa dentro dos componentes. A partir daí, a decisão de quanta estrutura adicionar fica muito mais fácil, porque deixa de ser religião e vira resposta a um problema concreto.
Se você quiser um próximo passo prático, escolha um componente que te dá medo e aplique o CLI test nele. Liste o que ali dentro é apresentação e o que é regra de negócio. Esse exercício, sozinho, já costuma mostrar a primeira função que merece sair do componente e virar um módulo de domínio. No próximo texto eu pego essa ideia e mostro como ela vira três camadas concretas — Presentation, Domain e Data — num exemplo de catálogo com carrinho.
Se isso fez sentido, eu escrevo sobre arquitetura de frontend, liderança técnica e carreira. Me acha no LinkedIn e no GitHub.