# Architecture Backend — NestJS Hexagonale --- ## Structure des couches ``` apps/backend/src/ ├── domain/ # Logique métier PURE — aucun import NestJS/TypeORM │ ├── entities/ # 17 entités métier (private constructor + static create()) │ ├── value-objects/ # Objets valeur immuables (Email, Money, BookingNumber...) │ ├── services/ # Services métier purs (csv-rate-price-calculator) │ ├── exceptions/ # Exceptions domaine │ └── ports/ │ ├── in/ # Interfaces use-case (execute()) │ └── out/ # Interfaces repository/SPI (token constants) │ ├── application/ # Couche API — dépend UNIQUEMENT du domain │ ├── controllers/ # REST controllers (Swagger + class-validator) │ ├── dto/ # Data Transfer Objects │ ├── mappers/ # Domain ↔ DTO (méthodes statiques) │ ├── guards/ # JwtAuthGuard, RolesGuard, ApiKeyOrJwtGuard │ ├── decorators/ # @Public(), @Roles(), @CurrentUser() │ ├── filters/ # DomainExceptionFilter │ ├── interceptors/ # PerformanceMonitoringInterceptor │ ├── gateways/ # WebSocket Socket.IO (notifications) │ ├── services/ # Services applicatifs (audit, notification, export, webhooks) │ └── [feature]/ # Modules par feature (auth, bookings, rates, etc.) │ └── infrastructure/ # Adaptateurs externes — dépend UNIQUEMENT du domain ├── persistence/typeorm/ │ ├── entities/ # ORM entities (*.orm-entity.ts) │ ├── repositories/# Implémentations TypeORM │ ├── mappers/ # ORM ↔ Domain (static toOrm/toDomain/toDomainMany) │ └── migrations/ # Migrations TypeORM ├── carriers/ # 5 connecteurs carriers + CSV loader ├── cache/ # Adaptateur Redis ├── email/ # MJML + Nodemailer ├── storage/ # S3/MinIO (+ csv-storage/) ├── stripe/ # Paiements et abonnements ├── external/ # Pappers (SIRET), ECU Worldwide ├── pdf/ # Génération PDF pdfkit ├── security/ # Utilitaires sécurité └── monitoring/ # Sentry + Pino ``` --- ## Entités domaine (17) | Entité | Fichier | |--------|---------| | ApiKey | api-key.entity.ts | | AuditLog | audit-log.entity.ts | | BlogPost | blog-post.entity.ts | | Booking | booking.entity.ts | | Carrier | carrier.entity.ts | | Container | container.entity.ts | | CsvBooking | csv-booking.entity.ts | | CsvRate | csv-rate.entity.ts | | InvitationToken | invitation-token.entity.ts | | License | license.entity.ts | | Notification | notification.entity.ts | | Organization | organization.entity.ts | | Port | port.entity.ts | | RateQuote | rate-quote.entity.ts | | Subscription | subscription.entity.ts | | User | user.entity.ts | | Webhook | webhook.entity.ts | --- ## Pattern entité domaine ```typescript // Private constructor + static create() factory export class Booking { private constructor(private readonly props: BookingProps) {} static create(props: Omit): Booking { return new Booking({ ...props, bookingNumber: BookingNumber.generate(), status: BookingStatus.DRAFT, }); } // Les mutations retournent une nouvelle instance (immuabilité) updateStatus(newStatus: BookingStatus): Booking { return new Booking({ ...this.props, status: newStatus }); } } ``` --- ## Pattern repository ```typescript // domain/ports/out/booking.repository.ts export const BOOKING_REPOSITORY = 'BookingRepository'; export interface BookingRepository { findById(id: string): Promise; save(booking: Booking): Promise; findByOrganization(orgId: string, filters: BookingFilters): Promise; } // infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts @Injectable() export class TypeOrmBookingRepository implements BookingRepository { constructor( @InjectRepository(BookingOrmEntity) private readonly repo: Repository, ) {} // ... } ``` --- ## Conventions de nommage | Couche | Suffix | Exemple | |--------|--------|---------| | Domain entity | .entity.ts | booking.entity.ts | | Value object | .vo.ts | money.vo.ts | | In port | .use-case.ts | create-booking.use-case.ts | | Out port | .repository.ts | booking.repository.ts | | ORM entity | .orm-entity.ts | booking.orm-entity.ts | | ORM mapper | .mapper.ts | booking.mapper.ts | | Controller | .controller.ts | bookings.controller.ts | | DTO | .dto.ts | create-booking-request.dto.ts | | App mapper | .mapper.ts | booking.mapper.ts | --- ## Règles critiques - **Jamais** d'import NestJS/TypeORM dans `domain/` - **Jamais** de type `any` dans le backend (strict mode) - **Jamais** modifier une migration déjà appliquée - **Toujours** valider les DTOs avec class-validator - **Toujours** créer un mapper séparé pour ORM ↔ Domain - `DATABASE_SYNC` est hard-codé à `false` — utiliser les migrations --- ## Modules NestJS (app.module.ts) **Guards globaux** (toutes routes) : - `JwtAuthGuard` — JWT obligatoire sauf routes `@Public()` - `CustomThrottlerGuard` — rate limiting **Feature modules** : Auth · Rates · Ports · Bookings · CsvBookings · Organizations · Users · Dashboard · Audit · Notifications · Webhooks · GDPR · Admin · Subscriptions · ApiKeys · Blog · Logs **Infrastructure modules** : CacheModule · CarrierModule · SecurityModule · CsvRateModule · StripeModule · PdfModule · StorageModule · EmailModule --- ## Alias de chemins (tsconfig.json) ```json { "@domain/*": ["src/domain/*"], "@application/*": ["src/application/*"], "@infrastructure/*": ["src/infrastructure/*"] } ``` --- ## Configuration TypeScript - `strict: true` - `strictNullChecks: true` - `strictPropertyInitialization: false` (exception pour ORM entities) - `noImplicitAny: true`