# 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<BookingProps, 'bookingNumber' | 'status'>): 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<Booking | null>;
  save(booking: Booking): Promise<Booking>;
  findByOrganization(orgId: string, filters: BookingFilters): Promise<Booking[]>;
}

// infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts
@Injectable()
export class TypeOrmBookingRepository implements BookingRepository {
  constructor(
    @InjectRepository(BookingOrmEntity)
    private readonly repo: Repository<BookingOrmEntity>,
  ) {}
  // ...
}
```

---

## 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`
