Voltar ao Blog
UX/UI Design30 de agosto de 202610 min de leitura

Atomic Design na Prática: Além da Teoria

DR

Doug Ribeiro

10 min de leitura

Atomic Design é uma lente útil. É uma estrutura de pastas terrível. Parei de impor literalmente, e minhas próprias codebases ficaram mais fáceis de navegar desde então.

Onde o Mapeamento Quebra

A hierarquia de cinco níveis faz sentido intuitivo em um slide de conferência. Na prática, as fronteiras ficam borradas no primeiro dia. Um TextField é um átomo? Ele contém um label, um input, um span de texto auxiliar e uma mensagem de erro. São quatro elementos. Pelas regras estritas do Atomic, é uma molécula. Mas conceitualmente é uma primitiva única, e tratá-lo como molécula significa que ele vive em uma pasta diferente dos seus outros primitivos de formulário.

DataTable tem o mesmo problema no nível de organismo. A paginação faz parte do organismo da tabela ou é um organismo separado composto junto? A resposta muda dependendo de se você abstrai a tabela como um componente standalone ou sempre a associa com controles. Nenhuma resposta está errada. A taxonomia simplesmente não te dá uma regra.

O segundo modo de falha é que o Atomic Design estrito trata átomos como stateless. O modelo de componentes do Angular é construído em torno de comportamento. Um Button sem estado de loading ou estado de disabled não é um átomo útil em um produto financeiro. Empurrar esse comportamento para a camada de molécula significa que coisas simples ficam desnecessariamente complicadas, e você acaba com abstrações no nível de molécula que existem apenas para guardar estado que pertencia ao átomo.

typescript
// A Button that carries its own state, and should
import { Component, Input } from '@angular/core';
import { SpinnerComponent } from '../spinner/spinner.component';

@Component({
  selector: 'ds-button',
  imports: [SpinnerComponent],
  template: `
    <button
      [class]="buttonClasses"
      [disabled]="disabled || loading"
      [attr.aria-busy]="loading"
    >
      @if (loading) {
        <ds-spinner size="sm" />
      }
      <ng-content />
    </button>
  `
})
export class ButtonComponent {
  @Input() variant: 'primary' | 'secondary' | 'ghost' = 'primary';
  @Input() size: 'sm' | 'md' | 'lg' = 'md';
  @Input() disabled = false;
  @Input() loading = false;

  get buttonClasses(): string {
    return [
      'btn',
      `btn--${this.variant}`,
      `btn--${this.size}`,
      this.loading ? 'btn--loading' : ''
    ].filter(Boolean).join(' ');
  }
}

O Modelo de Três Camadas Que Realmente Funciona

Eu uso três camadas ao invés de cinco. Camada de design system: componentes presentacionais reutilizáveis em qualquer projeto. Camada de feature: componentes específicos do domínio reutilizáveis dentro do projeto atual. Camada de página: componentes smart no nível da rota que compõem as outras duas.

Camada de páginaComponentes smart no nível da rota. Conectam o estado à UI. 1 Camada de featureComponentes específicos do domínio. Reutilizáveis dentro da feature. 2 Camada de design systemPresentacionais, com pouco estado, reutilizáveis em qualquer projeto. S… 3
Atomic Design (5 níveis)Átomos. Button, Input, LabelMoléculas. TextField, SearchBarOrganismos. Header, DataTableTemplates. PageLayoutPáginas. DashboardPageOnde fica o ButtonGroup?Modelo de 3 camadasDesign System. Primitivos compa…Feature. Componentes de domínioPágina. Composição no nível da …Sem taxonomia ambíguaBate com a estrutura de pastas …Espelha a organização das págin…VS

Isso encaixa direto na estrutura de pastas e bibliotecas de um projeto Angular standalone. Nenhum NgModule pra alinhar, só três diretórios e uma regra sobre pra que lado os imports podem apontar. Desenvolvedores novos entendem no primeiro dia. A pergunta que eles fazem, "isso pertence ao design system ou à feature de transações?", é muito mais fácil de responder do que "isso é uma molécula ou um organismo?".

text
src/
  lib/                        # Design system layer (publishable library)
    components/
      button/
      form-field/
      data-table/
      navigation-bar/
    tokens/
    styles/

  features/
    transactions/
      components/
        transaction-card/     # Domain-specific, not reusable outside transactions
        transaction-filter/
      pages/
        transaction-list-page/
        transaction-detail-page/

    accounts/
      components/
        account-summary/
      pages/
        accounts-overview-page/

Atomic Design Ainda Tem um Papel no Figma

No Figma, o vocabulário é mais útil porque componentes Figma não carregam lógica de negócio. Organizar uma biblioteca Figma com átomos, moléculas e organismos como convenções de nomenclatura funciona bem. O erro é exigir um mapeamento 1:1 para a estrutura Angular. A biblioteca Figma representa o sistema visual. A biblioteca Angular representa a implementação técnica. Elas se mantêm alinhadas através de nomes de tokens e convenções de nomenclatura de componentes, não por paridade de estrutura de pastas.

Na Wonderlabs eu mantinha tanto a biblioteca de componentes Figma quanto a biblioteca de componentes Angular para a app de cartão de crédito. Concordamos em nomes cedo: PascalCase, substantivo primeiro, sem nomes genéricos. Um componente chamado Card no Figma que na verdade é um SpendingLimitCard em todo uso deveria se chamar SpendingLimitCard nos dois lugares. Essa única convenção evitou mais confusão de handoff do que qualquer metodologia estrutural. Mais tarde, no BMO, trabalhei com o design system do banco, mantido por outro time, o que me colocou do lado consumidor. A regra vale igual desse lado: sempre que o nome no Figma e o seletor Angular batem, ninguém precisa de uma tabela de tradução.

Use Atomic Design para pensar sobre seu sistema, não para organizar suas pastas. A taxonomia é valiosa. A hierarquia estrita geralmente não é.

O Teste Real

A única pergunta que vale a pena fazer sobre uma arquitetura de componentes é: um desenvolvedor que entrou na semana passada consegue encontrar o arquivo certo em menos de 30 segundos, e saber imediatamente qual é a responsabilidade daquele arquivo? Se consegue, sua estrutura está funcionando. Se está debatendo se algo é uma molécula ou um organismo, você otimizou para a taxonomia ao invés do resultado.

  • Camada de design system: stateless ou com estado mínimo, sem lógica de domínio, reutilizável em qualquer produto
  • Camada de feature: consciente do domínio, reutilizável dentro do domínio da feature, pode usar componentes do design system livremente
  • Camada de página: smart, nível de rota, uma por rota, compõe a partir das duas camadas, conecta ao state management
  • Nomenclatura: PascalCase, substantivo primeiro, específico ao invés de genérico. SpendingLimitCard, e não Card
  • Nomes no Figma e Angular devem ser iguais. Faça uma sessão de alinhamento de nomenclatura antes de qualquer um abrir uma ferramenta

Referências

UX DesignDesign SystemsAtomic DesignFigma