Como criar componentes React com uma arquitetura escalável usando Atomic Design

Bruno Nardini
Ship It!
Published in
10 min readDec 13, 2021
Organização de blocos para construir diferentes produtos — Ilustração adaptada do livro Atomic Design

A maior dificuldade em projetos React é a organização dos componentes e diretórios no projeto. Mesmo as pessoas mais seguras da arquitetura escolhida ao iniciar o projeto, elas começam a ter dificuldade com a complexidade conforme o projeto vai crescendo.

Qualquer manutenção ou melhoria que deveria ser simples começa a se tornar complexa, fica mais difícil novas pessoas ingressarem no projeto, os testes de unidade se tornam ainda mais difíceis de fazer e manter.

Neste artigo, eu demonstro como podemos utilizar o Atomic Design para administrar essa complexidade, favorecendo a legibilidade, escalabilidade e flexibilidade do código.

O que é Atomic Design?

O Atomic Design é uma metodologia criada por Brad Frost, ela é composta por cinco níveis distintos trabalhando juntos para criar sistemas de design de interface de uma maneira mais deliberada e hierárquica. Os cinco níveis do Atomic Design são:

  1. Átomos (Atoms) são elementos da interface do usuário que não podem ser mais decompostos e servem como blocos de construção elementares de uma interface.
  2. Moléculas (Molecules) são coleções de átomos que formam componentes da interface relativamente simples.
  3. Organismos (Organisms) são componentes relativamente complexos que formam seções distintas de uma interface.
  4. Modelos (Templates) colocam os componentes em um layout e demonstram a estrutura do design.
  5. Páginas (Pages) aplicam conteúdo real aos modelos e articulam variações para demonstrar a interface final e testar a resiliência do Design System.
O Atomic Design consiste em átomos, moléculas, organismos, modelos e páginas.

Ao contrário do que muitas pessoas interpretam, o Atomic Design não é um processo linear, ele é um modelo mental para nos ajudar a pensar em nossas interfaces de usuário como um todo coeso e uma coleção de partes ao mesmo tempo.

Antes o que existia como guia era o famoso artigo Presentational and Container Components do Dan Abramov que nos mostrou a importância de criar componentes de apresentação separados de componentes com comportamento e lógica. A ideia era muito inovadora para época, nos permitia testar separadamente a parte visual da lógica de negócio.

Porém, era comum ver projetos onde se criavam vários componentes pequenos, como botões, campos, alertas, etc, e depois tentava conectar tudo de uma vez só em componentes maiores com regras de negócio. Os componentes menores eram fáceis de testar, mas os maiores ficavam bem complexos.

O Atomic Design trouxe a perspectiva que a construção dos componentes poderiam ser em camadas, construindo unidades maiores a partir das unidades menores.

Divisão em camadas da interface do Instagram usando o modelo do Atomic Design

Além de reduzir a complexidade de componentes maiores, fica mais fácil padronizar e reutilizar os elementos comuns da interface do usuário.

Estrutura de componentes

Antes de entrar no assunto de estrutura, quero ressaltar que eu não acredito em fórmulas prontas e fixas para nenhum tipo de arquitetura de software, acredito em arquiteturas emergentes, onde a estrutura deve evoluir junto com o software. Mas isso não significa que devemos ser desorganizados, ou que magicamente o desenho da estrutura vai surgir, pelo contrário, requer um alto comprometimento com a qualidade do projeto.

“Essencialmente, acho que muitas dessas áreas são padrões que aprendemos ao longo dos anos. À medida que seu conhecimento sobre os padrões aumenta, você deve ter uma ideia razoável de como usá-los. No entanto, a principal diferença é que não se espera que essas decisões arquitetônicas iniciais sejam imutáveis, ou melhor, a equipe sabe que pode errar em suas decisões iniciais e deve ter a coragem de corrigi-las. […]”
Is Design Dead? de Martin Fowler (tradução livre)

Tendo isso em mente, podemos usar o modelo mental do Atomic Design como guia para pensar nas decisões da arquitetura. Pensando do jeito React, podemos começar a pensar pela a plenitude da interface, olhar uma ou várias páginas e ir dividindo em unidades menores.

Fluxo de construção unindo o modelo React de pensar com o Atomic Design.

Se você estiver construindo um Design System, que abordo em seguida, você pode fazer o exercício de chegar até os átomos, mas se você já estiver no desenvolvimento do produto final, desça conforme for surgindo a necessidade.

Inicialmente os componentes terão as interações exemplificadas na imagem abaixo.

Modelo em que uma camada só pode acessar a próxima camada abaixo

Cada página é um componente que terão um ou mais modelos como filhos (children), que por sua vez terão um ou mais organismos como filhos e assim por diante até os átomos. Isso evita a pergunta mais comum: “Em qual camada eu crio esse componente?”, porque a resposta sempre será “Na camada abaixo!”.

Entenda como camada uma relação de hierarquia entre os componentes, um componente como filho de outro, não é uma organização de arquivos e diretórios, esse assunto abordarei no próximo tópico.

Ainda no começo vai começar a surgir a necessidade de reaproveitar partes comuns entre os componentes, mas que as camadas não são próximas, como por exemplo um organismo que precisa de um átomo, ou um modelo que precisa de uma molécula. Abaixo tem uma imagem que exemplifica como essa nova regra afeta a relação entre os componentes.

Modelo em que todas as camadas podem acessar qualquer camada abaixo

Existe uma troca ao tomar essa decisão. O primeiro modelo parece ser mais organizado se olharmos a imagem, e realmente é se pensarmos só na relação entre os componentes, mas exigirá uma organização muito bem trabalhada, muito bem pensada, para não crescer de forma exagerada o número de componentes, senão vai começar a surgir componentes só para cobrir o buraco entre as camadas.

O segundo modelo inverte o problema, você cria uma flexibilidade maior para criar só os componentes necessários, mas ganha uma complexidade maior. Essa complexidade não é barata, normalmente é aqui que as pessoas se perdem, aumenta a dúvida de qual camada cada componente deve ficar, a refatoração acaba sempre envolvendo mover os componentes de camadas, entre outras dores.

Estrutura de diretórios

O jeito mais simples de começar um projeto é criar um diretório fonte (src) para colocar o código e começar a criar os arquivos direto nele, quando começar a incomodar o número de arquivos que se torna o momento certo para começar a criar os diretórios, pois os arquivos irão ressaltar o que há de comum entre eles e se torna mais fácil a decisão de como agrupá-los.

Como o modelo mental do Atomic Design sugere uma divisão por camadas, você vai acabar sendo induzido a criar diretórios com os nomes de seus cinco níveis: atoms, molecules, organisms, templates e pages. Não há nenhum problema nessa forma de organizar, eu mesmo já fiz assim em alguns projetos, porém, você deve considerar alguns pontos:

  • Crie o diretório só quando surgir a necessidade, assim evita uma organização prematura e inibe o surgimento de outro tipo de organização que poderia ser melhor para o projeto.
  • Deixe claro que essa estrutura está fazendo sentido no momento em que ela foi feita, mas está em aberto para uma reestruturação quando fizer sentido, isso vai dar liberdade para as pessoas evoluírem a arquitetura conforme o crescimento do projeto. Faça isso na documentação.
  • Não presuma que todo mundo que irá trabalhar no projeto saiba o que são esses nomes, documente o racional por trás dessa decisão e o que deveria estar em cada diretório.

Independente de como será organizado, documente as decisões de todos os diretórios para que a próxima pessoa saiba do racional para manter ou evoluir o padrão atual. Esse é o maior desafio da arquitetura emergente, comunicar bem as decisões já tomadas. O melhor lugar para fazer isso é colocar a documentação versionada junto com o projeto, assim ambos evoluem juntos, um simples README.md na raiz do projeto com a descrição de cada diretório já é o suficiente para começar.

Mesmo em projetos grandes, evite um grande aninhamento de diretórios, além de dificultar a localização dos arquivos, dificulta mover os arquivos entre os diretórios e ao fazer as importações. A menos que você tenha um motivo muito convincente para usar uma estrutura profunda de pastas, considere limitar-se a um máximo de três ou quatro pastas aninhadas em um único projeto.

Se a quantidade de átomos e moléculas crescerem significativamente, ou caso surja a necessidade de compartilhar os componentes entre projetos, é o momento de começar a avaliar a extração desses componentes para uma UI Library, do qual abordarei no tópico a seguir sobre Design System.

Usando o Atomic Design para construir um Design System

Um Design System é um guia central que agrupa todos os elementos que permitirão os times projetar, realizar e desenvolver um produto. Todo produto tem um design, criar um Design System é definir um design intencional para o produto, do começo ao fim.

O mais famoso com certeza é o Material Design da Google, que popularizou o termo e é uma influência até hoje. Ele tem o conceito de componentes que são blocos interativos para a criação de uma interface de usuário, que se assemelha ao conceito de componentes do React, o casamento perfeito dos dois é o MUI (antes conhecido como Material UI), que é uma UI Library em React que implementa os padrões do Material Design.

UI Library é um conceito mais antigo que o Design System, que se popularizou com o Bootstrap, que consiste em uma biblioteca com componentes (blocos) já definidos para construir uma interface de usuário (UI — User Interface). Isso simplificou muito o desenvolvimento front-end, pois, além de padronizar os elementos da interface, é muito código já pronto para usar.

A decisão de criar um design intencional, definir um Design System, acaba surgindo cedo ou tarde em um projeto front-end. Quando esse momento chega, a decisão se resume em dois caminhos: criar uma UI Library do zero ou criar a partir de uma existente.

Ao utilizar uma UI Library já pronta, como MUI ou Bootstrap, tentar forçar uma arquitetura completa do Atomic Design pode trazer mais problema do que benefícios, porque a UI Library já possui uma estrutura que não pode se encaixar. Limite-se a criar uma biblioteca que entregue moléculas ou organismos já prontos, isso vai fornecer um controle maior na abstração.

Mas se optar por criar uma UI Library do zero, utilize as sugestões que dei nos tópicos anteriores, com a exceção que o exercício de criar os componentes vai ter que ir sempre até os átomos, pois eles serão os primeiros a serem entregues. É como seguir o caminho oposto do design, você começa a construir do nível menor e vai subindo até a página.

Não organize seu projeto front-end igual seu projeto back-end

Em uma aplicação WEB, por mais que o back-end e o front-end sejam partes de um mesmo produto, cada um tem sua particularidade que demanda uma organização diferente.

O back-end é responsável por receber, retornar e enviar dados. Esses dados podem ser persistidos, transacionados, processados, enfileirados, não importa, tudo gira em torno dos dados.

Já o front-end é responsável pela interface com o usuário, é onde os dados se transformam em informações, e vice-versa. O foco está no usuário, em como ele visualiza a informação e como ele interage com a interface.

Por exemplo, uma listagem de usuários, clientes e funcionários são 3 operações distintas no back-end, a organização do código que lida com cada uma dessas operações vão estar separadas para facilitar a manutenção da regra de negócio. Mas no front-end, apesar de ter uma tela para exibir cada uma dessas listagens, visualmente elas podem ser idênticas, então será que vale a pena triplicar os componentes visuais só porque os valores da informação mudam? Essas informações já não são dinâmicas de qualquer forma?

Ainda neste exemplo, podemos ter uma página para cada listagem: UserListPage, CustomerListPage e EmployeeListPage. Mas, se visualmente a tela é a mesma, podemos ter só um template chamado ListPage, ter um organismo para cada elemento desse template, e assim por diante nas camadas do Atomic Design.

Exemplo de um template único para listagens.

Então por isso que o Atomic Design é um modelo que faz sentido para o front-end, mas é difícil entender para quem está acostumado com arquiteturas back-end, ele propõe uma construção em camadas de elementos visuais da interface do usuário.

Até para testar fica mais simples. As mudanças de páginas da tabela, os eventos ao clicar nos botões, mudança dos filtros, tudo isso pode ser construído e testado uma única vez, em um só lugar, depois reutilizado em diferentes páginas.

A organização dos diretórios por funcionalidades, no front-end, pode fazer sentido para as partes que lidam com os dados e regras de negócio, como as rotas, serviços, hooks, integrações com o back-end, por exemplo. O importante é separar o que são regras de negócio e o que são componentes visuais, cada um com sua organização.

Componentes Atômicos

O Atomic Design possui os componentes classificados como Átomos, que são as menores unidades da interface a serem reutilizadas, mas todos os componentes devem ser atômicos.

Na programação, o conceito de atomicidade é usado para representar uma série indivisível e irredutível de operações de tal forma que tudo ocorre ou nada ocorre. Então a atomicidade é sobre completude, é isso que buscamos em componentes atômicos.

Um componente visual tem que ser completo por si só, deve ser independente de regras de negócio, comportamentos externos e dados dinâmicos. Um componente até pode conseguir interagir com outros componentes, mas não pode estar acoplado de forma que ele não consiga funcionar sozinho.

Uma forma de atingir essa independência é aplicar composição nos componentes (Compound Components), em que cada componente engloba somente o seu próprio estado e comportamento, mas ainda fornece o controle da renderização de suas partes variáveis.

A utilização de ferramentas como Storybook e Styleguidist auxiliam nessa tarefa, além de induzir um design mais independente dos componentes, o desenvolvimento fica mais produtivo, a documentação fica mais rica e favorece o compartilhamento.

O Atomic Design, apesar de simples, nos leva para um universo de teoria que pode auxiliar na construção de arquiteturas sustentáveis e escaláveis. Espero que este artigo tenha sido útil nessa jornada, que você e seu time possam criar grandes produtos juntos. Vá em frente e seja atômico!

Aqui na RD Station trabalhamos com um design system e micro frontends, todos criados com uma arquitetura intencional, mas emergente. Usamos o Atomic Design como referência em alguns projetos, e outros chegamos até a criar a estrutura de diretórios com os nomes dos níveis, a arquitetura de micro frontends nos permitiu essa pluralidade. Se tiver interesse em se juntar a nós nesta aventura, temos vagas!

--

--

Bruno Nardini
Ship It!

Staff Software Engineer at Pipefy | Teacher NardiniAcademy.com | Blogger BrunoNardini.com | Husband & Father | Guitar Player | Build and teach the WEB