Num app pequeno, qualquer estrutura funciona. O problema aparece quando o time cresce, as telas se multiplicam e cada mudança parece quebrar três coisas distantes. Arquitetura de front-end é o conjunto de decisões que mantém um app grande compreensível, consistente e barato de evoluir — e é o que se espera de um sênior conduzir.
Componentização: do átomo à página
A base de tudo é quebrar a UI em peças reutilizáveis. O atomic design dá um vocabulário útil: átomos (botão, input), moléculas (campo de busca = input + botão), organismos (um cabeçalho completo), templates e páginas. Não é dogma — é uma forma de pensar composição: peças pequenas e burras embaixo, peças que orquestram em cima.
Um bom componente faz uma coisa e não sabe onde será usado. No momento em que ele "conhece" a tela que o contém, virou acoplamento disfarçado de reúso.
Design system e design tokens
Um design system é a biblioteca compartilhada de componentes + regras de uso que garante que botão, cor e espaçamento sejam iguais em todo o produto. No coração dele estão os design tokens: variáveis nomeadas para valores de design (--color-primary, --space-md). Trocar a marca inteira vira mudar tokens, não caçar hex codes pelo código.
:root {
--color-primary: #4f46e5;
--space-md: 16px;
}
.btn { background: var(--color-primary); padding: var(--space-md); }
Organização por feature, não por tipo
Muitos projetos colapsam organizando por tipo de arquivo (uma pasta gigante de componentes, outra de hooks, outra de utils). Em escala, organize por feature: tudo que pertence ao "checkout" — componentes, estado, chamadas de API, testes — vive junto. Assim você lê uma funcionalidade num lugar só, e deletá-la é apagar uma pasta.
src/
features/
checkout/ (componentes, hooks, api, testes)
perfil/
shared/ (design system, utils genéricos)
Escolha da gerência de estado
Não existe "a melhor" — existe a adequada à natureza do dado. Estado de servidor (dados de API) pede ferramentas de cache como React Query. Estado de UI local fica no componente. Estado global de cliente (tema, sessão) pede Context ou um store leve. O erro comum do sênior iniciante é jogar tudo num Redux gigante. Separe por tipo de estado e a complexidade despenca.
Fronteiras de módulo
O que mantém um app grande sadio são fronteiras claras: cada feature expõe uma API pública (um index) e esconde o resto. Outras features importam só o que foi exposto. Como apartamentos num prédio — você usa o corredor comum, não atravessa a sala do vizinho. Isso permite refatorar o interior de um módulo sem quebrar o mundo.
O trade-off do monorepo
Monorepo é manter vários pacotes/apps num único repositório. Tem vantagens reais, mas custos reais também:
| Vantagens | Custos |
|---|---|
| Código compartilhado fácil | Tooling de build mais complexo |
| Refatoração atômica (um PR) | CI fica lento sem cache esperto |
| Versão única, sem drift | Repositório grande, mais governança |
A decisão é contextual: times pequenos ganham pouco e pagam o tooling; organizações com muito código compartilhado ganham muito. É comum em entrevista de sênior pedir justamente para você defender essa escolha — e o esperado não é a resposta "certa", é o raciocínio sobre trade-offs.
Como estruturar um app grande, em resumo
- Componentes pequenos e desacoplados, compostos de baixo para cima.
- Design system + tokens como única fonte de verdade visual.
- Organize por feature, com fronteiras de módulo explícitas.
- Estado segmentado por tipo (servidor, UI, global).
- Monorepo só quando o ganho de compartilhamento supera o custo de tooling.
Conclusão
Arquitetura de front-end é gerir acoplamento: peças pequenas, fronteiras claras, estado segmentado e visual centralizado em tokens. Decisões como monorepo são trade-offs, não verdades. Com a estrutura sólida, a próxima conversa de sênior é entregar isso rápido para o usuário.
In a small app, any structure works. The problem shows up when the team grows, screens multiply and every change seems to break three distant things. Front-end architecture is the set of decisions that keeps a large app understandable, consistent and cheap to evolve — and it's what a senior is expected to lead.
Componentization: from atom to page
The foundation of everything is breaking the UI into reusable pieces. Atomic design offers a useful vocabulary: atoms (button, input), molecules (search field = input + button), organisms (a complete header), templates and pages. It's not dogma — it's a way of thinking about composition: small, dumb pieces at the bottom, orchestrating pieces on top.
A good component does one thing and doesn't know where it will be used. The moment it "knows" the screen that contains it, it has become coupling disguised as reuse.
Design system and design tokens
A design system is the shared library of components + usage rules that ensures buttons, colors and spacing are the same across the whole product. At its heart are design tokens: named variables for design values (--color-primary, --space-md). Changing the entire brand becomes changing tokens, not hunting hex codes through the codebase.
:root {
--color-primary: #4f46e5;
--space-md: 16px;
}
.btn { background: var(--color-primary); padding: var(--space-md); }
Organize by feature, not by type
Many projects collapse by organizing by file type (one giant folder of components, another of hooks, another of utils). At scale, organize by feature: everything that belongs to "checkout" — components, state, API calls, tests — lives together. That way you read a feature in one place, and deleting it means deleting a folder.
src/
features/
checkout/ (components, hooks, api, tests)
perfil/
shared/ (design system, generic utils)
Choosing state management
There is no "best" — there's the one that fits the nature of the data. Server state (API data) calls for caching tools like React Query. Local UI state stays in the component. Global client state (theme, session) calls for Context or a lightweight store. The typical mistake of the novice senior is throwing everything into a giant Redux. Separate by type of state and the complexity plummets.
Module boundaries
What keeps a large app healthy are clear boundaries: each feature exposes a public API (an index) and hides the rest. Other features import only what was exposed. Like apartments in a building — you use the common hallway, you don't walk through your neighbor's living room. This lets you refactor the inside of a module without breaking the world.
The monorepo trade-off
A monorepo means keeping several packages/apps in a single repository. It has real advantages, but real costs too:
| Advantages | Costs |
|---|---|
| Easy shared code | More complex build tooling |
| Atomic refactoring (one PR) | CI gets slow without smart caching |
| Single version, no drift | Large repository, more governance |
The decision is contextual: small teams gain little and pay for the tooling; organizations with lots of shared code gain a lot. Senior interviews often ask you to defend exactly this choice — and what's expected isn't the "right" answer, it's the reasoning about trade-offs.
How to structure a large app, in short
- Small, decoupled components, composed from the bottom up.
- Design system + tokens as the single source of visual truth.
- Organize by feature, with explicit module boundaries.
- State segmented by type (server, UI, global).
- Monorepo only when the sharing gains outweigh the tooling cost.
Conclusion
Front-end architecture is about managing coupling: small pieces, clear boundaries, segmented state and visuals centralized in tokens. Decisions like monorepo are trade-offs, not truths. With a solid structure, the next senior conversation is delivering it fast to the user.