Atomic Design es un lente útil. Es una estructura de carpetas terrible. Dejé de aplicarlo literalmente, y mis propios codebases han sido más fáciles de navegar desde entonces.
Dónde se quiebra el mapeo
La jerarquía de cinco niveles tiene sentido intuitivo en una diapositiva de conferencia. En la práctica, las fronteras se difuminan desde el primer día. ¿Un TextField es un átomo? Contiene un label, un input, un span de texto de ayuda, y un mensaje de error. Son cuatro elementos. Por reglas estrictas de Atomic sería una molécula. Pero conceptualmente es una sola primitiva, y tratarlo como molécula significa que vive en una carpeta diferente a tus otras primitivas de formulario.
DataTable tiene el mismo problema a nivel de organismo. ¿La paginación es parte del organismo de tabla o un organismo separado compuesto junto a él? La respuesta cambia dependiendo de si abstraes la tabla como un componente standalone o siempre la emparejas con controles. Ninguna respuesta es incorrecta. La taxonomía simplemente no te da una regla.
El segundo modo de fallo es que Atomic Design estricto trata a los átomos como stateless. El modelo de componentes de Angular está construido alrededor del comportamiento. Un Button sin un estado de carga o un estado deshabilitado no es un átomo útil en un producto financiero. Empujar ese comportamiento hacia la capa de molécula significa que cosas simples se vuelven innecesariamente complicadas, y terminas con abstracciones a nivel de molécula que existen solo para contener estado que pertenecía al átomo.
// 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(' ');
}
}El modelo de tres capas que realmente funciona
Uso tres capas en lugar de cinco. Capa de design system: componentes presentacionales reutilizables en cualquier proyecto. Capa de feature: componentes específicos del dominio reutilizables dentro del proyecto actual. Capa de página: componentes inteligentes a nivel de ruta que componen las otras dos.
Esto se mapea directo a la estructura de carpetas y librerías de un proyecto Angular standalone. No hay NgModules contra los cuales alinearlo, solo tres directorios y una regla sobre hacia dónde pueden apuntar los imports. Los nuevos developers lo entienden el primer día. La pregunta que hacen, '¿esto pertenece al design system o al feature de transacciones?', es mucho más fácil de responder que '¿esto es una molécula o un organismo?'.
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 todavía tiene un rol en Figma
En Figma, el vocabulario es más útil porque los componentes de Figma no llevan lógica de negocio. Organizar una librería de Figma con átomos, moléculas y organismos como convenciones de nombres funciona bien. El error es exigir un mapeo 1:1 a la estructura de Angular. La librería de Figma representa el sistema visual. La librería de Angular representa la implementación técnica. Se mantienen alineadas a través de nombres de tokens y convenciones de nombres de componentes, no por paridad de estructura de carpetas.
En Wonderlabs mantuve tanto la librería de componentes de Figma como la librería de componentes de Angular para la app de tarjeta de crédito. Acordamos nombres temprano: PascalCase, sustantivo primero, sin nombres genéricos. Un componente llamado Card en Figma que en realidad es un SpendingLimitCard en cada uso debería llamarse SpendingLimitCard en ambos lugares. Esa sola convención previno más confusión de handoff que cualquier metodología estructural. Más tarde, en BMO, trabajé con el design system del banco, mantenido por otro equipo, lo que me puso del lado consumidor. La regla importa igual desde ahí: cuando un nombre en Figma y un selector de Angular coinciden, nadie necesita una tabla de traducción.
Usa Atomic Design para pensar sobre tu sistema, no para organizar tus carpetas. La taxonomía es valiosa. La jerarquía estricta usualmente no lo es.
La prueba real
La única pregunta que vale la pena hacer sobre una arquitectura de componentes es: ¿un developer que se unió la semana pasada puede encontrar el archivo correcto en menos de 30 segundos, y saber inmediatamente de qué es responsable ese archivo? Si puede, tu estructura funciona. Si están debatiendo si algo es una molécula o un organismo, has optimizado para la taxonomía en lugar del resultado.
- Capa de design system: stateless o mínimamente stateful, sin lógica de dominio, reutilizable en cualquier producto
- Capa de feature: consciente del dominio, reutilizable dentro del dominio del feature, puede usar componentes del design system libremente
- Capa de página: inteligente, a nivel de ruta, una por ruta, compone de ambas capas, se conecta al state management
- Naming: PascalCase, sustantivo primero, específico sobre genérico. SpendingLimitCard, no Card
- Los nombres en Figma y Angular deberían coincidir. Haz una sesión de alineación de nombres antes de que alguien abra una herramienta
Referencias
- Brad Frost. Atomic Web Design. https://bradfrost.com/blog/post/atomic-web-design/
- Angular libraries guide. https://angular.dev/guide/libraries
- Nx module boundaries. https://nx.dev/concepts/decisions/project-dependency-rules