Turborepo ou Nx pour gérer un monorepo TypeScript
Un monorepo réunit plusieurs applications, bibliothèques et services dans un dépôt Git unique. Cette organisation évite de synchroniser manuellement des versions partagées et fournit une vue commune du code, des tests et des déploiements. Elle devient surtout utile lorsqu’une API, une interface web et des paquets internes évoluent ensemble.
Pour une équipe TypeScript, le monorepo simplifie les imports entre composants, centralise les règles de qualité et permet de vérifier plusieurs projets avec une seule commande. Chaque paquet est soit publié, soit consommé en interne, avec des frontières explicites.
Turborepo et Nx répondent à ce besoin avec deux approches complémentaires. Turborepo se concentre sur l’exécution et la mise en cache des tâches. Nx ajoute un graphe de projets, des règles de dépendances et des générateurs. Le bon choix dépend moins du nombre de fichiers que de la maturité du dépôt et des contraintes de l’équipe.
Pourquoi un monorepo TypeScript est pertinent
Le premier bénéfice est la cohérence des versions. Un paquet d’authentification peut être mis à jour avec l’interface, le serveur et les tests sans publier chaque changement séparément. Les dépendances restent traçables et les régressions sont détectées avant la fusion.
La centralisation réduit la duplication des configurations. Les règles TypeScript, les scripts de qualité et les images Docker suivent une convention commune. Un nouveau membre découvre ainsi les mêmes commandes et les mêmes garde-fous.
Cette mutualisation exige toutefois une discipline claire. Les responsabilités restent réparties entre les paquets, les frontières contrôlées et les caches isolés pour éviter la production d’artefacts incompatibles. Un dossier sans propriétaire et sans tests devient vite un point de blocage pour toute l’équipe.
Structurer les paquets et les espaces de travail
Le gestionnaire de paquets fournit la base du dépôt. pnpm-workspace.yaml liste les espaces de travail, tandis que pnpm --recursive exécute un script dans chaque paquet. pnpm réduit les duplications de dépendances, mais la configuration du graphe reste indépendante de cet outil.
Chaque unité vit dans un répertoire dédié, généralement apps/ pour les produits et packages/ pour les bibliothèques. Les dépendances entre unités sont déclarées dans leur manifeste, et le code partagé possède une API publique courte. Cette discipline évite de transformer un monorepo en collection de dossiers difficile à suivre.
Pour un service HTTP, le guide sur une API REST avec Zod montre comment isoler les routes, les schémas et les erreurs dans des paquets réutilisables. Le même découpage permet à l’interface, aux tests d’intégration et au worker de consommer les mêmes types sans dupliquer les validations.
Partager une configuration TypeScript fiable
À la racine, tsconfig.base.json fixe strict, la version cible, les options de modules et les chemins communs. Les paquets étendent cette base plutôt que de recopier des réglages. Une configuration centralisée reste facile à faire évoluer, à condition de ne pas y ajouter des règles propres à une seule application.
Chaque paquet peut définir un tsconfig.json dédié et activer les références de projet lorsque le mode composite est utile. Les alias @scope/... doivent être déclarés à la fois pour TypeScript et pour le bundler. Nx peut inférer certaines tâches, mais les frontières de compilation restent explicites dans les fichiers du dépôt.
Dans un module Node.js chargé des newsletters, le guide consacré à la gestion des désinscriptions montre l’intérêt de typer un schéma partagé pour les événements, les erreurs et la confirmation du retrait. Ce même contrat réduit les divergences entre API, base de données et traitements asynchrones.
Accélérer les tâches avec Turborepo
Dans turbo.json, chaque tâche déclare ses entrées, ses sorties et ses dépendances. Une règle dependsOn: ["^build"] attend la compilation des dépendances avant l’application, tandis que les sorties déclarées deviennent des artefacts cachables. Turborepo stocke ainsi les résultats réutilisables sur le disque local et, si nécessaire, dans un cache distant.
La CLI permet de cibler une chaîne précise avec turbo run build --filter=@scope/web. L’option --affected compare les changements Git et ne traite que les paquets concernés par la modification. Dans une branche longue, cette sélection réduit fortement le temps de validation.
Dans un dépôt qui contient aussi un service d’expérimentation, un paquet TypeScript peut piloter l’entraînement, sérialiser les paramètres et contrôler les métriques. La régression ridge régularisée offre un exemple concret de concept à documenter dans les tests et le README du service.
Renforcer la gouvernance avec Nx
Nx construit un graphe de projets à partir des fichiers sources, des manifestes et des plugins. Les frontières peuvent être exprimées par tags, puis contrôlées par les règles de modules ESLint. Cette gouvernance devient utile lorsque plusieurs équipes publient des bibliothèques sans autoriser des imports internes non maîtrisés.
Des commandes comme nx affected -t test et nx run-many -t build ciblent les changements ou un ensemble de projets. Le cache local .nx/cache évite de recompiler un résultat identique, tandis que Nx Cloud peut distribuer l’exécution et centraliser les artefacts.
Pour préparer le suivi de production, un inventaire de pages de statut facilite la correspondance entre les services, les incidents et la communication. Les tags Nx peuvent associer chaque projet à son propriétaire, à son environnement et à sa procédure de restauration.
Choisir selon l’équipe et le projet
Turborepo reste généralement plus simple à adopter lorsque l’équipe veut surtout une exécution rapide des scripts, un cache fiable et une configuration lisible. Il laisse une grande liberté sur l’organisation du dépôt et demande de construire soi-même certaines conventions.
Nx devient plus pertinent quand la gouvernance, les générateurs et plusieurs langages doivent coexister. Ses fonctions avancées couvrent des besoins plus larges, tandis que certaines offres et licences sont commerciales. Son graphe струкure? Avoid. "Son graphe structure aussi les relations entre les applications et les bibliothèques."
Le choix peut se faire sur des critères concrets : taille de l’équipe, nombre de services, langages, besoin de génération, coût du cache distribué et exigences de traçabilité. Les deux outils exécutent TypeScript, respectent les espaces de travail et s’intègrent avec les gestionnaires de paquets courants. Une migration progressive, limitée à une application pilote, permet de mesurer le temps de construction et la fréquence des échecs de cache avant une décision définitive.
Industrialiser la chaîne de livraison
Dans la chaîne de livraison, le monorepo fournit une base commune pour la vérification du typage, les tests, la construction et la publication des images. La configuration CI doit utiliser un lockfile figé, conserver les caches entre les executions et associer les artefacts au commit exact. Les tâches limitées aux projets affectés réduisent la consommation de ressources sans masquer les tests transversaux.
Pour un paquet de persistance, la comparaison des bases NoSQL aide à choisir une technologie selon le modèle de données et les besoins de disponibilité. Cette sélection doit précéder la création du service, car une migration de schéma ou de requête devient coûteuse lorsque plusieurs applications dépendent du même stockage.
La publication des changements demande également une coordination claire. Les versions, les notes de livraison et les migrations de base de données doivent être automatisées, puis validées dans un environnement temporaire. Les métriques de durée, de taux d’échec du cache et de fréquence des restaurations donnent une image concrète du fonctionnement du dépôt.
Lancez un pilote dès aujourd’hui : créez un dépôt pnpm, ajoutez Turborepo ou Nx, migrez une seule application et comparez pendant quatre semaines le temps de build, la validité du cache et la clarté des frontières.