5 min de lecture - Construire des systèmes NestJS évolutifs et maintenables
Backend Architecture & Monorepo Strategy
Le débat monorepo contre multi-repo a évolué. Pour les équipes qui développent des systèmes backend complexes avec NestJS, le monorepo est passé du statut d'expérimentation à celui de recommandation par défaut — non pas parce que c'est tendance, mais parce que l'outillage a suffisamment mûri pour le rendre pertinent à toutes les échelles.
Ce guide couvre les patterns, l'outillage et les compromis pour faire fonctionner des applications NestJS dans un monorepo, de la configuration initiale jusqu'au déploiement en production.
Ce que vous allez apprendre
- Quand un monorepo a du sens (et quand ce n'est pas le cas) pour des projets NestJS
- Comment mettre en place un monorepo NestJS piloté par Nx, de A à Z
- Les patterns de bibliothèques partagées pour l'authentification, la journalisation et les modèles de données
- La communication entre microservices au sein d'un monorepo
- Des stratégies CI/CD qui maintiennent des temps de build maîtrisés
L'essentiel
Un monorepo NestJS est particulièrement pertinent lorsque plusieurs services partagent du code (authentification, DTO, utilitaires), lorsque les équipes ont besoin de modifications atomiques inter-services, et lorsque l'on souhaite un pipeline CI/CD unique. Utilisez Nx pour la mise en cache des calculs, l'analyse des commandes « affected » et la visualisation du graphe de dépendances. Évitez le monorepo si les services sont réellement indépendants et ne partagent aucun code.
Qu'est-ce qu'un monorepo (et ce qu'il n'est pas)
Un monorepo est un dépôt unique contenant plusieurs projets qui peuvent être construits, testés et déployés indépendamment. Ce n'est pas un monolithe — chaque projet du dépôt peut avoir son propre pipeline de déploiement et son propre runtime.
Les géants de la tech (Google, Meta, Microsoft) utilisent des monorepos depuis des années. Ce qui a changé, c'est que des outils comme Nx et Turborepo ont rendu cette approche viable pour des équipes de 5 personnes, et plus seulement pour des équipes de 5 000.
Pourquoi NestJS s'intègre naturellement au modèle monorepo
L'architecture modulaire de NestJS — où chaque fonctionnalité est un module avec des imports et exports explicites — s'articule naturellement avec les bibliothèques partagées d'un monorepo :
Gestion simplifiée des dépendances. Tous les services partagent un seul node_modules. Plus de dérive de versions entre services utilisant des versions différentes d'une même bibliothèque.
Commits atomiques. La mise à jour d'un DTO partagé et de tous les services qui l'utilisent se fait en un seul commit. Plus besoin de coordonner des pull requests sur plusieurs dépôts.
Partage de code sans publication. Les bibliothèques partagées (guards d'authentification, intercepteurs de journalisation, entités TypeORM) sont importées directement — aucun registre npm privé n'est nécessaire.
Outillage unifié. Une seule configuration ESLint, une seule configuration Prettier, un seul test runner, un seul pipeline CI.
Mettre en place un monorepo NestJS piloté par Nx
Nx est l'outil de monorepo le plus mature de l'écosystème Node.js. Voici une configuration pratique :
npx create-nx-workspace@latest my-platform --preset=nest
Cela génère un workspace avec une seule application NestJS. Ajoutez d'autres applications et bibliothèques selon vos besoins :
# Add a second NestJS service
nx g @nx/nest:application api-gateway
# Create a shared library
nx g @nx/nest:library shared-auth
nx g @nx/nest:library shared-dto
Structure du workspace
my-platform/
├── apps/
│ ├── api-gateway/ # HTTP gateway service
│ │ └── src/
│ ├── billing-service/ # Billing microservice
│ │ └── src/
│ └── notification-service/
│ └── src/
├── libs/
│ ├── shared-auth/ # Auth guards, strategies, decorators
│ ├── shared-dto/ # Request/response DTOs, validation
│ ├── shared-database/ # TypeORM entities, migrations
│ └── shared-logging/ # Logger interceptors, correlation IDs
├── nx.json
├── tsconfig.base.json
└── package.json
Alias de chemins TypeScript
Nx configure automatiquement les alias de chemins afin que les bibliothèques partagées soient importables proprement :
// In any app or library
import { AuthGuard, CurrentUser } from '@my-platform/shared-auth'
import { CreateUserDto, UserResponseDto } from '@my-platform/shared-dto'
import { LoggingInterceptor } from '@my-platform/shared-logging'
Patterns de bibliothèques partagées
Bibliothèque d'authentification
Une bibliothèque d'authentification partagée fournit les guards, décorateurs et stratégies utilisés par l'ensemble des services :
// libs/shared-auth/src/lib/guards/jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context)
}
}
// libs/shared-auth/src/lib/decorators/current-user.decorator.ts
export const CurrentUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest()
return request.user
})
Bibliothèque de DTO avec validation
Les DTO partagés garantissent des formats de requête/réponse cohérents entre les services :
// libs/shared-dto/src/lib/user.dto.ts
export class CreateUserDto {
@IsEmail()
email: string
@IsString()
@MinLength(8)
password: string
}
export class UserResponseDto {
id: string
email: string
createdAt: Date
}
Communication entre microservices
Au sein d'un monorepo, les microservices NestJS peuvent communiquer via plusieurs couches de transport :
TCP pour les appels synchrones de service à service (faible latence, configuration simple).
RabbitMQ ou Redis pour une communication événementielle (découplée, résiliente).
gRPC pour une communication haute performance basée sur des schémas (typage fort, sérialisation efficace).
// api-gateway calling billing-service via TCP
@Injectable()
export class BillingClient {
private client: ClientProxy
constructor() {
this.client = ClientProxyFactory.create({
transport: Transport.TCP,
options: { host: 'localhost', port: 3001 },
})
}
calculateBill(userId: string): Observable<BillingAmount> {
return this.client.send('calculate_bill', { userId })
}
}
Stratégies CI/CD pour les monorepos
La principale préoccupation avec les monorepos, c'est le temps de build. Nx répond à ce problème avec deux fonctionnalités :
Les commandes « affected ». Ne construire, tester et linter que les projets impactés par une modification donnée :
nx affected --target=build
nx affected --target=test
Mise en cache des calculs. Nx met en cache les résultats des tâches localement et (avec Nx Cloud) à distance. Si une bibliothèque n'a pas changé, son résultat de build est réutilisé.
Un pipeline CI concret :
# .github/workflows/ci.yml
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: npm ci
- run: npx nx affected --target=lint --base=origin/main
- run: npx nx affected --target=test --base=origin/main
- run: npx nx affected --target=build --base=origin/main
Quand le monorepo n'est pas le bon choix
Un monorepo ajoute de la charge opérationnelle. Évitez-le si :
- Les services sont réellement indépendants et ne partagent aucun code
- Les équipes appartiennent à des organisations différentes avec des cadences de release différentes
- Vous avez moins de deux services déployables
- Votre infrastructure CI ne peut pas absorber un dépôt plus volumineux
Démarrez petit, ajoutez des services au fur et à mesure que la complexité l'exige
NestJS et Nx forment une combinaison efficace pour les équipes qui construisent des backends multi-services. Le monorepo supprime la charge de coordination (pull requests inter-dépôts, dérive de versions, configurations dupliquées), tandis que Nx maintient des temps de build sous contrôle. Commencez avec une seule application et une bibliothèque partagée, validez le workflow, puis ajoutez des services au fur et à mesure que la complexité l'exige. Besoin d'aide pour concevoir l'architecture de votre monorepo NestJS ? Parlons-en.
Parlons-en.
Exceev accompagne les startups et les PME dans leurs projets de conseil, d'outillage open source et de logiciels prêts pour la production.