7.5 KiB
7.5 KiB
Architecture — Xpeditis
Xpeditis est une plateforme B2B SaaS de réservation de fret maritime, construite en monorepo avec une architecture hexagonale stricte côté backend.
Vue d'ensemble système
┌─────────────────────────────────────────────────────────────┐
│ Frontend (Next.js 14 + App Router) │
│ React 18 · TanStack Query · Socket.IO · next-intl (i18n) │
└──────────────────────────┬──────────────────────────────────┘
│ HTTPS / WSS
┌──────────────────────────▼──────────────────────────────────┐
│ Backend (NestJS 10) │
│ JWT Auth · Rate Limiting · Swagger OpenAPI │
└────┬──────────┬──────────┬──────────┬──────────┬────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Rates Bookings Users Carriers CSV System
Service Service Service Service Service
│ │ │ │ │
└──────────┴──────────┴──────────┴──────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
PostgreSQL 15 Redis 7 MinIO (S3)
(données) (cache 15min) (documents)
Architecture hexagonale (backend)
apps/backend/src/
├── domain/ # Logique métier pure — zéro dépendance framework
│ ├── entities/ # 17 entités métier
│ ├── value-objects/ # Objets valeur immuables
│ ├── services/ # Services métier purs
│ ├── ports/in/ # Interfaces use-case (execute())
│ └── ports/out/ # Interfaces repository/SPI
│
├── application/ # Controllers, DTOs, Guards
│ ├── controllers/ # REST controllers avec Swagger
│ ├── dto/ # DTOs class-validator
│ ├── mappers/ # Domain ↔ DTO
│ ├── guards/ # JwtAuthGuard, RolesGuard, ApiKeyGuard
│ ├── gateways/ # WebSocket (Socket.IO)
│ └── services/ # Services applicatifs (audit, notification)
│
└── infrastructure/ # Adaptateurs externes
├── persistence/ # TypeORM (entities, repositories, mappers, migrations)
├── carriers/ # Connecteurs carriers + CSV loader
├── cache/ # Adaptateur Redis
├── email/ # MJML + Nodemailer
├── storage/ # S3/MinIO + CSV storage
├── stripe/ # Abonnements et paiements
├── external/ # Pappers (SIRET)
├── pdf/ # Génération PDF (pdfkit)
└── monitoring/ # Sentry, logging Pino
Règles de dépendance :
- Domain : aucune dépendance externe (TypeScript pur)
- Application : dépend uniquement du domain
- Infrastructure : dépend uniquement du domain
- Le flux de dépendances pointe toujours vers le centre
Stack technique
Backend
| Composant | Technologie |
|---|---|
| Framework | NestJS 10.x |
| Langage | TypeScript 5.3+ strict |
| ORM | TypeORM 0.3.x |
| Base de données | PostgreSQL 15+ |
| Cache | Redis 7+ (ioredis) |
| Auth | JWT (15min) + Refresh tokens (7j) + OAuth2 |
| Auth alternative | API Keys (Argon2 hash) |
| WebSocket | Socket.IO |
| Nodemailer + MJML templates | |
| pdfkit | |
| Paiements | Stripe |
| Stockage fichiers | S3 / MinIO |
| Validation | class-validator + class-transformer |
| Docs API | Swagger/OpenAPI |
| Logging | nestjs-pino (pino-pretty dev) |
| Monitoring | Sentry |
| i18n | nestjs-i18n |
| Circuit breaker | opossum (5s timeout carriers) |
Frontend
| Composant | Technologie |
|---|---|
| Framework | Next.js 14 (App Router) |
| Langage | TypeScript |
| Styling | Tailwind CSS |
| State serveur | TanStack Query v5 |
| Tables | TanStack Table v8 + Virtual |
| Formulaires | react-hook-form + zod |
| Temps réel | Socket.IO client |
| Graphiques | recharts |
| Cartes | react-leaflet |
| i18n | next-intl (fr, en) |
| Éditeur riche | Tiptap |
| État global | zustand |
| Animations | framer-motion |
Modules NestJS
Guards globaux : JwtAuthGuard (toutes les routes protégées par défaut), CustomThrottlerGuard
Feature modules :
- Auth, Rates, Ports, Bookings, CsvBookings, Organizations, Users
- Dashboard, Audit, Notifications, Webhooks, GDPR, Admin
- Subscriptions, ApiKeys, Blog, Logs
Infrastructure modules :
- CacheModule (Redis), CarrierModule, SecurityModule
- CsvRateModule, StripeModule, PdfModule, StorageModule, EmailModule
Flux clés
Recherche de tarifs (FCL)
POST /api/v1/rates/search
→ Vérification cache Redis (TTL 15min)
→ Si cache miss : appel parallel carriers API (5s timeout + circuit breaker)
→ Normalisation résultats → stockage Redis
→ Réponse JSON paginée
Réservation standard
POST /api/v1/bookings
→ Validation DTO → Génération WCM-YYYY-XXXXXX
→ Persistance PostgreSQL
→ Audit log
→ Notification WebSocket
→ Déclenchement webhooks
→ Email confirmation (MJML)
→ PDF booking
Portail Carrier (CSV Bookings)
Admin crée CSV booking → assigne carrier
→ Email magic link (1h expiry)
→ Carrier s'authentifie via token
→ Accept/Reject → Activité logguée
Abonnements
Stripe webhook → /api/v1/subscriptions/webhook
→ Vérification signature HMAC
→ Mise à jour subscription + license
Sécurité
| Mesure | Implémentation |
|---|---|
| Rate limiting | 100 req/min global, 5/min auth, 30/min search |
| Password hashing | Argon2id |
| JWT | Access 15min + Refresh 7j avec rotation |
| API Keys | Argon2 hash, préfixe xped_ |
| Brute force | Exponential backoff après 3 échecs |
| Headers sécurité | Helmet.js (CSP, HSTS, XSS) |
| Validation | class-validator sur tous les DTOs |
| RBAC | 5 rôles : ADMIN, MANAGER, USER, VIEWER, CARRIER |
| CORS | Origines strictes |
| Upload fichiers | Validation MIME, max 10MB |
| GDPR | Export/suppression données utilisateur |
Performances
- Recherche tarifs (avec cache) : < 2s (p90) — ~500ms observé
- Création booking : < 3s — ~1s observé
- Dashboard (5k bookings) : < 1s
- Cache hit ratio cible : > 90%
Déploiement
L'application est containerisée (Dockerfile dans chaque app).
- Développement : docker-compose (PostgreSQL + Redis + MinIO)
- Production : Portainer / Docker Swarm ou Kubernetes (Hetzner)
- CI/CD : GitHub Actions (
.github/workflows/)
Voir ../deployment/portainer.md et ../deployment/hetzner/README.md.
Dernière mise à jour : Mai 2026