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
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.mdTaskFlow, 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) :
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
| Plan | TaskFlow | Raison |
|---|---|---|
docs/PLAN.md | Absent | Le plan est publié sur ce site |
common/ : filters, interceptors, pipes, decorators | common/ : filters, interceptors, middleware, decorators, dto | Les 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.ts | Le CLI TypeORM ne démarre pas Nest : il a sa propre DataSource, qui partage ses options avec l'application (J5) |
| — | app.setup.ts | Les 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 racine | Plus .gitlab-ci.yml, .oxlintrc.json, .dockerignore, .gitignore, nest-cli.json, jest.config.ts | CI 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 module | Les 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).
| Module | Fichier | Exporte | Importé par |
|---|---|---|---|
CommonModule | common/common.module.ts | CLOCK (module @Global()) | Visible partout |
DatabaseModule | database/database.module.ts | — (configure TypeORM) | AppModule |
UsersModule | users/users.module.ts | UsersService | AuthModule |
AuthModule | auth/auth.module.ts | — | AppModule |
ProjectsModule | projects/projects.module.ts | ProjectsService (pas le store) | TasksModule |
TasksModule | tasks/tasks.module.ts | — | AppModule |
NotificationsModule | notifications/notifications.module.ts | — | AppModule ; importe NotificationsQueueModule si REDIS_URL est défini |
HealthModule | health/health.controller.ts | — | AppModule |
DemoModule | demo/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), etcommon/dto/pour ce qui est partagé (pagination).entities/dans le module qui possède la table :ProjectMembervit dansprojects/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 danscommon/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ément | Fichier | Classe |
|---|---|---|
| Module | projects.module.ts | ProjectsModule |
| Contrôleur | projects.controller.ts | ProjectsController |
| Service | projects.service.ts | ProjectsService |
| Entité | project.entity.ts, project-member.entity.ts | Project, ProjectMember |
| DTO | create-project.dto.ts (plan, nest g) ou project.dto.ts (TaskFlow) | CreateProjectDto |
| Filtre | all-exceptions.filter.ts | AllExceptionsFilter |
| Intercepteur | logging.interceptor.ts | LoggingInterceptor |
| Middleware | correlation-id.middleware.ts | CorrelationIdMiddleware |
| Stratégie Passport | jwt.strategy.ts | JwtStrategy |
| Enum | task-status.enum.ts | TaskStatus |
| Événement | task-status-changed.event.ts | TaskStatusChangedEvent |
| Test unitaire | projects.service.spec.ts | — |
| Test e2e | app.e2e-spec.ts | — |
| Migration | 1793093891613-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.tspour les contrôleurs et les services (J1, CLI).
Les suffixes servent aux outils
Plusieurs configurations sélectionnent les fichiers par motif :
| Configuration | Motif | Effet |
|---|---|---|
jest.config.ts | src/.*\.spec\.ts$ | Fichiers de tests unitaires |
jest.config.ts (collectCoverageFrom) | *.service.ts, guards.ts, *.filter.ts, *.interceptor.ts | Fichiers 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.