MohandOussadi
Retour au blog
Monorepo et design system : retour d'expérience

Monorepo et design system : retour d'expérience

17 septembre 2024
3 min de lecture
Monorepo
Design System
Architecture

Quand une équipe maintient plusieurs applications (un site public, un back-office, une app mobile…), les mêmes composants finissent par être copiés-collés d'un projet à l'autre. Un bouton corrigé ici reste bogué là-bas, les couleurs divergent, et chaque mise à jour devient une chasse au trésor.

Sur un projet de plateforme pour courtiers, nous avons résolu ce problème en regroupant les applications et un design system commun dans un monorepo. Voici ce que nous avons appris.

Monorepo : de quoi parle-t-on ?

Un monorepo est un dépôt Git unique qui contient plusieurs projets (applications et librairies), chacun avec son propre package.json. Ce n'est pas un monolithe : les projets restent indépendants, mais ils partagent l'outillage, l'historique et peuvent dépendre les uns des autres sans passer par un registre npm.

Structure d'un monorepo : les applications consomment des packages partagés
Structure d'un monorepo : les applications consomment des packages partagés

Une structure qui a fait ses preuves

.
├── apps/
│   ├── web/          # site public (Next.js)
│   ├── admin/        # back-office
│   └── mobile/       # React Native
├── packages/
│   ├── ui/           # design system : composants + tokens
│   ├── config/       # ESLint, TypeScript, Tailwind partagés
│   └── api-client/   # client typé de l'API
├── pnpm-workspace.yaml
└── turbo.json

La règle : les `apps` dépendent des `packages`, jamais l'inverse, et les packages ne dépendent pas des apps. Cela garde un graphe de dépendances simple et évite les cycles.

Les outils

  • Les workspaces du gestionnaire de paquets (pnpm, npm ou Yarn) : ils lient les packages locaux entre eux. Avec pnpm, on déclare "@acme/ui": "workspace:*" dans l'app.
  • Un orchestrateur de tâches : Turborepo ou Nx exécutent build, lint et test dans le bon ordre, en parallèle, et mettent en cache les résultats. Si le package ui n'a pas changé, son build n'est pas relancé.
  • Lerna, historiquement l'outil de référence, est aujourd'hui maintenu par l'équipe de Nx et reste pertinent pour la publication et le versionnement de packages. Changesets est une alternative légère très répandue.
// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "lint": {},
    "test": { "dependsOn": ["^build"] }
  }
}

Le ^build signifie « construire d'abord les dépendances » : le design system est compilé avant les applications qui l'utilisent.

Construire le design system

  • Commencer par les tokens (couleurs, espacements, typographie, rayons) exposés en variables CSS. Ils permettent de changer de thème sans toucher aux composants.
  • Des composants accessibles par défaut : s'appuyer sur des primitives éprouvées (Radix UI, React Aria) plutôt que de réécrire la gestion du focus et du clavier.
  • Documenter avec Storybook : chaque composant a ses variantes visibles, ce qui sert de référence commune aux designers et aux développeurs.
  • Exporter proprement : un point d'entrée par composant (@acme/ui/button) facilite le tree-shaking.

Les pièges que nous avons rencontrés

  • Des versions de React différentes entre les apps : un seul exemplaire de React doit être résolu, sinon les hooks cassent. On le déclare en peerDependencies dans le design system.
  • Une CI qui reconstruit tout à chaque commit : sans cache distant ni filtrage des projets impactés (turbo run build --filter=...[origin/main]), les temps de build explosent.
  • Un design system qui devient un fourre-tout : un composant métier propre à une seule app n'a rien à y faire. Il doit rester générique.

Ce que ça nous a apporté

Le principal gain a été le time-to-market : une nouvelle application démarrait avec l'authentification, le thème et une vingtaine de composants prêts à l'emploi. Une correction dans le design system se propageait à toutes les apps en une seule pull request, testée de bout en bout.

Conclusion

Le monorepo n'est pas une solution miracle : il demande un outillage sérieux et de la discipline sur les dépendances. Mais dès qu'une équipe maintient plusieurs applications qui partagent une identité visuelle et du code métier, c'est l'un des investissements les plus rentables qu'on puisse faire.

Partager cet article