Skip to content

Structure du repo ​

Le plan propose une arborescence cible et quelques conventions de nommage. Cette page les met en regard de l'arborescence réelle de TaskFlow, explique les écarts et détaille les conventions, qui ne sont pas seulement esthétiques : plusieurs outils sélectionnent les fichiers par leur suffixe.

Arborescence ​

Structure cible du plan ​

text
taskflow/
├── docs/
│   └── PLAN.md            # ce document
├── src/
│   ├── main.ts
│   ├── app.module.ts
│   ├── common/            # filters, interceptors, pipes, decorators
│   ├── config/
│   ├── auth/
│   ├── users/
│   ├── projects/
│   ├── tasks/
│   ├── notifications/
│   └── database/
│       └── migrations/
├── test/                  # e2e
├── docker-compose.yml
├── Dockerfile
├── .env.example
├── LEARNINGS.md
└── README.md

TaskFlow, tel qu'il est ​

Le projet se trouve dans examples/ (sans node_modules/, dist/ ni coverage/, générés et ignorés par son propre .gitignore) :

text
examples/
├── src/
│   ├── main.ts                          démarrage : NestFactory.create
│   ├── app.module.ts                    module racine, providers globaux, middleware
│   ├── app.setup.ts                     réglages HTTP communs à main.ts et aux e2e, Swagger
│   ├── common/
│   │   ├── common.module.ts             module global, token CLOCK
│   │   ├── correlation-id.ts
│   │   ├── decorators/auth.decorators.ts         @Public(), @Roles(), @CurrentUser()
│   │   ├── dto/pagination.dto.ts                 PaginationQueryDto, Page<T>
│   │   ├── filters/all-exceptions.filter.ts      (+ .spec.ts)
│   │   ├── interceptors/logging.interceptor.ts   (+ .spec.ts)
│   │   ├── middleware/correlation-id.middleware.ts
│   │   └── time-zone.ts                 APP_TIME_ZONE, localDate()
│   ├── config/
│   │   ├── configuration.ts             config typée (registerAs)
│   │   └── env.validation.ts            schéma des variables d'environnement (+ .spec.ts)
│   ├── database/
│   │   ├── database.module.ts           TypeOrmModule.forRootAsync
│   │   ├── postgres-options.ts          options communes à l'app et au CLI
│   │   ├── data-source.ts               DataSource du CLI TypeORM
│   │   ├── seed.ts                      données de développement
│   │   └── migrations/1793093891613-InitialSchema.ts
│   ├── auth/
│   │   ├── auth.module.ts, auth.controller.ts, auth.service.ts (+ .spec.ts)
│   │   ├── jwt.strategy.ts
│   │   ├── guards.ts                    JwtAuthGuard, RolesGuard (+ .spec.ts)
│   │   └── dto/auth.dto.ts
│   ├── users/
│   │   ├── users.module.ts, users.controller.ts, users.service.ts (+ .spec.ts)
│   │   ├── role.enum.ts
│   │   └── entities/user.entity.ts
│   ├── projects/
│   │   ├── projects.module.ts, projects.controller.ts, projects.service.ts (+ .spec.ts)
│   │   ├── projects.store.ts            interface ProjectsStore + token PROJECTS_STORE
│   │   ├── in-memory-projects.store.ts  implémentation J2-J3
│   │   ├── typeorm-projects.store.ts    implémentation J4+, transactions
│   │   ├── dto/project.dto.ts
│   │   └── entities/project.entity.ts, project-member.entity.ts
│   ├── tasks/
│   │   ├── tasks.module.ts, tasks.controller.ts, tasks.service.ts (+ .spec.ts)
│   │   ├── tasks.scheduler.ts           cron OVERDUE
│   │   ├── task-status.enum.ts
│   │   ├── dto/task.dto.ts
│   │   ├── entities/task.entity.ts
│   │   └── events/task-status-changed.event.ts
│   ├── notifications/
│   │   ├── notifications.module.ts
│   │   ├── notifications.listener.ts    @OnEvent
│   │   └── notifications.queue.ts       module BullMQ et processor
│   ├── health/health.controller.ts      Terminus (+ HealthModule)
│   └── demo/event-loop.controller.ts    exercice du J1 (+ DemoModule), hors production
├── test/
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── docker-compose.yml
├── Dockerfile, .dockerignore, .gitignore
├── .gitlab-ci.yml
├── .env.example
├── .oxlintrc.json, nest-cli.json, jest.config.ts, tsconfig.json, tsconfig.build.json
├── package.json, package-lock.json
├── LEARNINGS.md
└── README.md

Écarts et raisons ​

PlanTaskFlowRaison
docs/PLAN.mdAbsentLe plan est publié sur ce site
common/ : filters, interceptors, pipes, decoratorscommon/ : filters, interceptors, middleware, decorators, dtoLes pipes intégrés (ParseUUIDPipe, ParseEnumPipe) suffisent ; le middleware de corrélation et le DTO de pagination sont partagés
database/migrations/Plus database.module.ts, postgres-options.ts, data-source.ts, seed.tsLe CLI TypeORM ne démarre pas Nest : il a sa propre DataSource, qui partage ses options avec l'application (J5)
—app.setup.tsLes réglages HTTP qui ne sont pas des providers (versioning, helmet, CORS) servent à la fois à main.ts et aux tests e2e
—health/, demo/Health checks Terminus du J11 ; exercice event loop du J1, chargé hors production
test/ (e2e)test/ pour les e2e, tests unitaires à côté du code (*.spec.ts)Convention de la CLI Nest
Fichiers à la racinePlus .gitlab-ci.yml, .oxlintrc.json, .dockerignore, .gitignore, nest-cli.json, jest.config.tsCI du J11, lint, contexte de l'image, fichiers hors de git, plugin Swagger, seuil de couverture
create-project.dto.ts (exemple de convention)dto/project.dto.ts regroupe les DTO du moduleLes DTO d'un module sont courts (quatre pour les projets, deux pour les tâches) : un seul fichier reste lisible. nest g resource génère un fichier par DTO, les deux se défendent

Conventions ​

Un module par domaine ​

Chaque domaine (utilisateurs, projets, tâches…) a son module, qui déclare ses contrôleurs et providers et n'exporte que ce dont les autres ont besoin. C'est l'équivalent d'un découpage « package by feature » en Spring, avec une différence de taille : rien n'est découvert automatiquement, chaque dépendance entre modules est un imports explicite (J3).

ModuleFichierExporteImporté par
CommonModulecommon/common.module.tsCLOCK (module @Global())Visible partout
DatabaseModuledatabase/database.module.ts— (configure TypeORM)AppModule
UsersModuleusers/users.module.tsUsersServiceAuthModule
AuthModuleauth/auth.module.ts—AppModule
ProjectsModuleprojects/projects.module.tsProjectsService (pas le store)TasksModule
TasksModuletasks/tasks.module.ts—AppModule
NotificationsModulenotifications/notifications.module.ts—AppModule ; importe NotificationsQueueModule si REDIS_URL est défini
HealthModulehealth/health.controller.ts—AppModule
DemoModuledemo/event-loop.controller.ts—AppModule, hors production

Les modules qui ne contiennent qu'un contrôleur (HealthModule, DemoModule) sont déclarés dans le même fichier que lui ; dès qu'un module a plusieurs providers, il a son propre *.module.ts.

Dossiers dto/ et entities/ ​

  • dto/ dans chaque module qui reçoit des données (projects/dto, tasks/dto, auth/dto), et common/dto/ pour ce qui est partagé (pagination).
  • entities/ dans le module qui possède la table : ProjectMember vit dans projects/entities, car c'est le module des projets qui gère ses membres.
  • D'autres sous-dossiers apparaissent quand le besoin existe : tasks/events/ pour les classes d'événements, et dans common/ un dossier par type de composant (filters/, interceptors/, middleware/, decorators/).

Nommage des fichiers et des classes ​

Fichiers en kebab-case avec le type en suffixe, classes en PascalCase avec le même suffixe :

ÉlémentFichierClasse
Moduleprojects.module.tsProjectsModule
Contrôleurprojects.controller.tsProjectsController
Serviceprojects.service.tsProjectsService
Entitéproject.entity.ts, project-member.entity.tsProject, ProjectMember
DTOcreate-project.dto.ts (plan, nest g) ou project.dto.ts (TaskFlow)CreateProjectDto
Filtreall-exceptions.filter.tsAllExceptionsFilter
Intercepteurlogging.interceptor.tsLoggingInterceptor
Middlewarecorrelation-id.middleware.tsCorrelationIdMiddleware
Stratégie Passportjwt.strategy.tsJwtStrategy
Enumtask-status.enum.tsTaskStatus
Événementtask-status-changed.event.tsTaskStatusChangedEvent
Test unitaireprojects.service.spec.ts—
Test e2eapp.e2e-spec.ts—
Migration1793093891613-InitialSchema.ts (horodatage généré)InitialSchema1793093891613

Autres conventions de TaskFlow :

  • constantes et tokens en UPPER_SNAKE_CASE : PROJECTS_STORE, CLOCK, TASK_STATUS_CHANGED ;
  • tables et colonnes en snake_case (project_members, due_date), propriétés en camelCase : le nom de colonne est donné explicitement (@Column({ name: 'due_date' })), car TypeORM reprend sinon le nom de la propriété ;
  • nest g module|controller|service <nom> crée les fichiers avec ces noms, plus un .spec.ts pour les contrôleurs et les services (J1, CLI).

Les suffixes servent aux outils ​

Plusieurs configurations sélectionnent les fichiers par motif :

ConfigurationMotifEffet
jest.config.tssrc/.*\.spec\.ts$Fichiers de tests unitaires
jest.config.ts (collectCoverageFrom)*.service.ts, guards.ts, *.filter.ts, *.interceptor.tsFichiers mesurés par la couverture
test/jest-e2e.json.e2e-spec.ts$Fichiers de tests e2e
src/database/data-source.ts*.entity.{ts,js}Entités vues par le CLI TypeORM (migrations)
Plugin Swagger (nest-cli.json)*.dto.ts, *.entity.ts (valeurs par défaut)Classes dont le plugin déduit le schéma OpenAPI

Un fichier qui s'écarte du suffixe échappe au motif, sans message. Exemples dans TaskFlow : le DTO de pagination s'appelle pagination.dto.ts pour que le plugin Swagger le lise et documente page et limit (J11) ; les deux guards sont regroupés dans auth/guards.ts, que collectCoverageFrom désigne par son nom (src/**/guards.ts) pour qu'il compte dans la couverture (J10, couverture). De même, une entité nommée project.ts serait invisible pour migration:generate.