L'Atomic Design est une grille de lecture utile. C'est une structure de dossiers terrible. J'ai arrêté de l'appliquer littéralement, et mes propres codebases sont plus faciles à naviguer depuis.
Quand le mapping s'effondre
La hiérarchie à cinq niveaux a du sens intuitivement dans une diapositive de conférence. En pratique, les frontières deviennent floues dès le premier jour. Un TextField est-il un atome ? Il contient un label, un input, un span de texte d'aide et un message d'erreur. Cela fait quatre éléments. Selon les règles strictes de l'Atomic Design, c'est une molécule. Mais conceptuellement c'est une seule primitive, et le traiter comme une molécule signifie qu'il vit dans un dossier différent de vos autres primitives de formulaire.
DataTable pose le même problème au niveau organisme. La pagination fait-elle partie de l'organisme tableau ou est-ce un organisme séparé composé à côté ? La réponse change selon que vous abstraisez le tableau comme un composant standalone ou que vous le couplez toujours avec des contrôles. Aucune réponse n'est fausse. La taxonomie ne vous donne tout simplement pas de règle.
Le second mode d'échec est que l'Atomic Design strict traite les atomes comme sans état. Le modèle de composants d'Angular est construit autour du comportement. Un Button sans état de chargement ni état désactivé n'est pas un atome utile dans un produit financier. Pousser ce comportement vers la couche molécule signifie que des choses simples deviennent inutilement compliquées, et vous vous retrouvez avec des abstractions de niveau molécule qui n'existent que pour porter un état qui appartenait à l'atome.
// 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(' ');
}
}Le modèle à trois couches qui fonctionne vraiment
J'utilise trois couches au lieu de cinq. Couche design system : composants de présentation réutilisables dans n'importe quel projet. Couche fonctionnalité : composants spécifiques au domaine réutilisables au sein du projet actuel. Couche page : composants intelligents au niveau de la route qui composent les deux autres.
Cela se pose directement sur la structure de dossiers et de bibliothèques d'un projet Angular standalone. Aucun NgModule à aligner avec, juste trois répertoires et une règle sur le sens dans lequel les imports ont le droit de pointer. Les nouveaux développeurs le comprennent le premier jour. La question qu'ils posent, 'cela appartient-il au design system ou à la fonctionnalité transactions ?', est beaucoup plus facile à répondre que 'est-ce une molécule ou un organisme ?'.
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/L'Atomic Design a toujours un rôle dans Figma
Dans Figma, le vocabulaire est plus utile parce que les composants Figma ne portent pas de logique métier. Organiser une bibliothèque Figma avec atomes, molécules et organismes comme conventions de nommage fonctionne bien. L'erreur est d'exiger un mapping 1:1 vers la structure Angular. La bibliothèque Figma représente le système visuel. La bibliothèque Angular représente l'implémentation technique. Elles restent alignées via les noms de tokens et les conventions de nommage des composants, pas par la parité de la structure de dossiers.
Chez Wonderlabs j'ai maintenu à la fois la bibliothèque de composants Figma et la bibliothèque de composants Angular pour l'application de carte de crédit. Nous nous sommes mis d'accord sur les noms tôt : PascalCase, nom en premier, pas de noms génériques. Un composant appelé Card dans Figma qui est en fait un SpendingLimitCard dans chaque usage devrait être nommé SpendingLimitCard aux deux endroits. Cette seule convention a prévenu plus de confusion au handoff que n'importe quelle méthodologie structurelle. Plus tard, chez BMO, j'ai travaillé avec le design system de la banque, détenu par une autre équipe, ce qui m'a placé du côté consommateur. La règle compte tout autant de ce côté-là : dès qu'un nom Figma et un sélecteur Angular correspondent, personne n'a besoin d'une table de correspondance.
Utilisez l'Atomic Design pour penser votre système, pas pour organiser vos dossiers. La taxonomie est précieuse. La hiérarchie stricte ne l'est généralement pas.
Le vrai test
La seule question qui vaille la peine d'être posée sur une architecture de composants est : est-ce qu'un développeur arrivé la semaine dernière peut trouver le bon fichier en moins de 30 secondes et savoir immédiatement de quoi ce fichier est responsable ? Si oui, votre structure fonctionne. S'il débat pour savoir si quelque chose est une molécule ou un organisme, vous avez optimisé pour la taxonomie au lieu du résultat.
- Couche design system : sans état ou état minimal, pas de logique de domaine, réutilisable dans n'importe quel produit
- Couche fonctionnalité : conscient du domaine, réutilisable au sein du domaine fonctionnel, peut utiliser les composants du design system librement
- Couche page : intelligente, au niveau de la route, une par route, compose à partir des deux couches, se connecte à la gestion d'état
- Nommage : PascalCase, nom en premier, spécifique plutôt que générique. SpendingLimitCard, pas Card
- Les noms Figma et Angular doivent correspondre. Faites une session d'alignement des noms avant que quiconque n'ouvre un outil
Références
- 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