Lab 26 : NX — Architecture Monorepo
📖 Ressources
Section intitulée « 📖 Ressources »🚀 Code de départ
Section intitulée « 🚀 Code de départ »Aucun — vous créerez un workspace NX depuis zéro.
Dans ce lab, vous mettrez en place un monorepo NX, générerez deux applications Angular, créerez une bibliothèque de composants partagés et utiliserez l’alias de chemin pour la consommer. Vous explorerez également le graphe de dépendances et la commande affected.
📝 Instructions
Section intitulée « 📝 Instructions »Étape 1 : Créer le workspace NX
Section intitulée « Étape 1 : Créer le workspace NX »npx create-nx-workspace@latest my-workspaceQuand demandé :
- Stack : Angular
- Type : Integrated Monorepo
- Nom de l’application : shop
- Format des styles : CSS (ou SCSS)
- Activer le cache distribué (Nx Cloud) : Non
my-workspace/├── apps/│ └── shop/├── libs/├── nx.json├── tsconfig.base.json└── package.jsonÉtape 2 : Démarrer l’application
Section intitulée « Étape 2 : Démarrer l’application »cd my-workspacenx serve shopOuvrez http://localhost:4200 — l’application Angular par défaut s’exécute.
Étape 3 : Générer une deuxième application
Section intitulée « Étape 3 : Générer une deuxième application »nx g @nx/angular:app adminVous avez maintenant deux applications indépendantes partageant le même dépôt :
apps/├── shop/└── admin/Exécutez chacune indépendamment :
nx serve shop # http://localhost:4200nx serve admin # http://localhost:4201Étape 4 : Générer une bibliothèque UI partagée
Section intitulée « Étape 4 : Générer une bibliothèque UI partagée »nx g @nx/angular:lib ui --directory=libs/uiNote (Nx v22+) : Passez
uicomme nom positionnel et--directory=libs/uicomme chemin de sortie complet. Évitez de combiner--name=uiet--directory=libs/uiensemble — Nx traite--directorycomme le chemin complet, et les deux flags peuvent produire un dossierlibs/ui/ui/imbriqué au lieu dulibs/ui/attendu.
Pourquoi libs/ et pas src/ ?
Un projet Angular standard garde tout dans src/. Dans un Integrated Monorepo Nx, la convention est de placer le code partageable dans le dossier racine libs/ afin que plusieurs applications (shop, admin, …) puissent l’importer sans artifices de chemins circulaires. Chaque bibliothèque est un projet TypeScript indépendant avec son propre project.json.
libs/ui vs libs/shop/shared-ui
| Chemin | Portée | À utiliser quand… |
|---|---|---|
libs/ui/ |
Workspace entier | Primitives UI génériques (boutons, icônes, typographie) utilisées par toutes les applications |
libs/shop/shared-ui/ |
Scope shop uniquement | Composants spécifiques au domaine shop qui ne doivent pas déborder dans d’autres scopes |
Utilisez libs/ui pour des briques pouvant appartenir à un design system. Utilisez libs/shop/shared-ui pour des widgets de panier, des cartes produit, ou tout ce qui est propre au shop.
Vérifier l’alias de chemin généré par Nx
Après l’exécution du générateur, ouvrez tsconfig.base.json et cherchez la nouvelle entrée sous paths :
{ "compilerOptions": { "paths": { "@my-workspace/ui": ["libs/ui/src/index.ts"] } }}C’est ainsi que TypeScript résout @my-workspace/ui vers les fichiers source réels dans libs/ui/src/index.ts. Aucun alias webpack ni résolveur personnalisé n’est nécessaire — il s’agit du mappage de chemins TypeScript standard.
⚠️ Le préfixe dépend du nom d’organisation saisi lors du
create-nx-workspace. Si vous avez choisiacme, l’alias sera@acme/ui. Lisez toujourstsconfig.base.json → compilerOptions.pathspour trouver l’alias exact de votre workspace avant de l’utiliser dans vos imports.
Tags et frontières de modules
Nx applique des règles d’import via @nx/enforce-module-boundaries dans eslint.config.mjs. Les bibliothèques doivent porter des tags pour que la règle sache quels scopes peuvent les importer.
Ouvrez libs/ui/project.json et vérifiez (ou ajoutez) le champ tags :
{ "tags": ["scope:shared", "type:ui"]}Sans scope:shared, ESLint bloquera toute application qui tente d’importer cette bibliothèque dès que des depConstraints sont configurées. Vous pouvez inspecter les règles de frontières dans eslint.config.mjs :
// eslint.config.mjs (extrait)"@nx/enforce-module-boundaries": [ "error", { "depConstraints": [ { "sourceTag": "scope:shop", "onlyDependOnLibsWithTags": ["scope:shop", "scope:shared"] }, { "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:admin", "scope:shared"] } ] }]Cela signifie que shop ne peut importer que des libs taguées scope:shop ou scope:shared. Taguez libs/ui avec scope:shared pour que les deux applications puissent l’utiliser librement.
Étape 5 : Créer un composant partagé
Section intitulée « Étape 5 : Créer un composant partagé »Générez un composant bouton dans la bibliothèque :
nx g @nx/angular:component libs/ui/src/lib/button/button --exportNote (Nx v22+) : Le flag
--projecta été supprimé du générateur de composants. Passez le chemin complet vers l’endroit où le composant doit être créé à la place.
Modifiez libs/ui/src/lib/button/button.component.ts :
import { Component, input } from '@angular/core';
@Component({ selector: 'lib-button', standalone: true, template: ` <button class="btn" [class]="variant()"> <ng-content /> </button> `, styles: [` .btn { padding: 8px 16px; border: none; cursor: pointer; border-radius: 4px; } .primary { background: #007bff; color: white; } .secondary { background: #6c757d; color: white; } `]})export class ButtonComponent { variant = input<'primary' | 'secondary'>('primary');}Assurez-vous qu’il est exporté depuis libs/ui/src/index.ts :
export * from './lib/button/button.component';Étape 6 : Utiliser la bibliothèque dans l’application shop
Section intitulée « Étape 6 : Utiliser la bibliothèque dans l’application shop »Dans apps/shop/src/app/app.component.ts, importez en utilisant l’alias trouvé dans tsconfig.base.json :
import { ButtonComponent } from '@my-workspace/ui'; // remplacez le préfixe si le vôtre est différent
@Component({ selector: 'app-root', standalone: true, imports: [ButtonComponent], template: ` <h1>Boutique</h1> <lib-button variant="primary">Ajouter au panier</lib-button> <lib-button variant="secondary">Voir les détails</lib-button> `})export class AppComponent {}nx serve shop — le bouton partagé s’affiche dans l’application shop.
Étape 7 : Visualiser le graphe de dépendances
Section intitulée « Étape 7 : Visualiser le graphe de dépendances »nx graphLe navigateur ouvre un graphe interactif. Vérifiez :
shopdépend deuiadminn’a pas encore de dépendances
Étape 8 : Exécuter les commandes affected
Section intitulée « Étape 8 : Exécuter les commandes affected »Faites une modification dans ButtonComponent, puis :
# Ne builder que ce qui a changé + ce qui en dépendnx affected --target=build
# Ne tester que ce qui a changé + ce qui en dépendnx affected --target=testNX détermine que modifier ui affecte shop (qui en dépend) — les deux sont donc reconstruits. admin n’est pas touché.
C’est le principal avantage en CI/CD : vous ne payez que pour ce qui a changé.

