Some checks failed
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 58s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Failing after 5m55s
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Deployment Summary (push) Has been skipped
CI/CD Pipeline / Deploy to Portainer (push) Has been skipped
CI/CD Pipeline / Discord Notification (Success) (push) Has been skipped
CI/CD Pipeline / Discord Notification (Failure) (push) Has been skipped
Reorganisation majeure de toute la documentation du projet pour ameliorer la navigation et la maintenance. ## Changements principaux ### Organisation (80 -> 4 fichiers .md a la racine) - Deplace 82 fichiers .md dans docs/ organises en 11 categories - Conserve uniquement 4 fichiers essentiels a la racine: * README.md, CLAUDE.md, PRD.md, TODO.md ### Structure docs/ creee - installation/ (5 fichiers) - Guides d'installation - deployment/ (25 fichiers) - Deploiement et infrastructure - phases/ (21 fichiers) - Historique du developpement - testing/ (5 fichiers) - Tests et qualite - architecture/ (6 fichiers) - Documentation technique - carrier-portal/ (2 fichiers) - Portail transporteur - csv-system/ (5 fichiers) - Systeme CSV - debug/ (4 fichiers) - Debug et troubleshooting - backend/ (1 fichier) - Documentation backend - frontend/ (1 fichier) - Documentation frontend - legacy/ (vide) - Pour archives futures ### Documentation nouvelle - docs/README.md - Index complet de toute la documentation (367 lignes) * Guide de navigation par scenario * Recherche rapide par theme * FAQ et commandes rapides - docs/CLEANUP-REPORT-2025-12-22.md - Rapport detaille du nettoyage ### Scripts reorganises - add-email-to-csv.py -> scripts/ - deploy-to-portainer.sh -> docker/ ### Fichiers supprimes - 1536w default.svg (11MB) - Fichier non utilise ### References mises a jour - CLAUDE.md - Section Documentation completement reecrite - docs/architecture/EMAIL_IMPLEMENTATION_STATUS.md - Chemin script Python - docs/deployment/REGISTRY_PUSH_GUIDE.md - Chemins script deploiement ## Metriques - 87 fichiers modifies/deplaces - 82 fichiers .md organises dans docs/ - 11MB d'espace libere - Temps de recherche reduit de ~5min a ~30s (-90%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
769 lines
20 KiB
Markdown
769 lines
20 KiB
Markdown
# Décisions Architecturales - Xpeditis
|
|
|
|
**Projet**: Xpeditis - Plateforme B2B SaaS maritime
|
|
**Format**: Architecture Decision Records (ADR)
|
|
**Dernière mise à jour**: 2025-12-22
|
|
|
|
---
|
|
|
|
## Index des décisions
|
|
|
|
| ID | Date | Titre | Status |
|
|
|----|------|-------|--------|
|
|
| ADR-001 | 2025-12-22 | Adoption de l'architecture hexagonale | ✅ Acceptée |
|
|
| ADR-002 | 2025-12-22 | Suppression dépendances NestJS du domain layer | 🟡 Proposée |
|
|
| ADR-003 | 2025-12-22 | Activation TypeScript strict mode frontend | 🟡 Proposée |
|
|
| ADR-004 | 2025-12-22 | Standardisation pattern data fetching (React Query) | 🟡 Proposée |
|
|
| ADR-005 | 2025-12-22 | Migration pagination client-side vers serveur | 🟡 Proposée |
|
|
|
|
**Légende des status**:
|
|
- ✅ Acceptée: Décision implémentée
|
|
- 🟡 Proposée: En attente d'implémentation
|
|
- ❌ Rejetée: Décision écartée
|
|
- 🔄 Superseded: Remplacée par une autre décision
|
|
|
|
---
|
|
|
|
## ADR-001: Adoption de l'architecture hexagonale
|
|
|
|
**Date**: 2025-12-22 (rétroactif - décision initiale du projet)
|
|
**Status**: ✅ Acceptée et implémentée
|
|
|
|
### Contexte
|
|
|
|
L'application Xpeditis nécessite:
|
|
- Une séparation claire entre la logique métier et les détails techniques
|
|
- La capacité de changer de framework sans réécrire le métier
|
|
- Une testabilité maximale du code domaine
|
|
- L'indépendance vis-à-vis des bases de données et APIs externes
|
|
|
|
### Décision
|
|
|
|
Adopter l'**architecture hexagonale (Ports & Adapters)** pour le backend NestJS avec:
|
|
|
|
**3 couches strictement séparées**:
|
|
1. **Domain**: Logique métier pure (zéro dépendance externe)
|
|
2. **Application**: Orchestration et points d'entrée (controllers, DTOs)
|
|
3. **Infrastructure**: Adapters externes (DB, cache, email, APIs)
|
|
|
|
**Règles de dépendance**:
|
|
- Infrastructure → Application → Domain
|
|
- Jamais l'inverse
|
|
- Les interfaces (ports) sont définies dans le domain
|
|
- Les implémentations (adapters) sont dans l'infrastructure
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- ✅ Domaine métier testable sans framework
|
|
- ✅ Changement de DB/ORM sans impact sur le métier
|
|
- ✅ Code domaine réutilisable
|
|
- ✅ Séparation claire des responsabilités
|
|
- ✅ Facilite l'onboarding des nouveaux développeurs
|
|
|
|
**Négatives**:
|
|
- ⚠️ Plus de fichiers à créer (ports, adapters, mappers)
|
|
- ⚠️ Courbe d'apprentissage initiale
|
|
- ⚠️ Verbosité accrue (mappers Domain ↔ ORM, Domain ↔ DTO)
|
|
|
|
**Risques**:
|
|
- ⚠️ Tentation de violer les règles (imports directs, shortcuts)
|
|
- ⚠️ Over-engineering pour des features simples
|
|
|
|
### Implémentation
|
|
|
|
**Structure adoptée**:
|
|
```
|
|
apps/backend/src/
|
|
├── domain/ # 🔵 Cœur métier (aucune dépendance)
|
|
│ ├── entities/
|
|
│ ├── value-objects/
|
|
│ ├── services/
|
|
│ ├── ports/
|
|
│ └── exceptions/
|
|
├── application/ # 🔌 Controllers & Use Cases
|
|
│ ├── controllers/
|
|
│ ├── dto/
|
|
│ ├── services/
|
|
│ └── mappers/
|
|
└── infrastructure/ # 🏗️ Adapters externes
|
|
├── persistence/
|
|
├── cache/
|
|
├── email/
|
|
└── carriers/
|
|
```
|
|
|
|
**Validation**:
|
|
- Tous les modules respectent la structure
|
|
- 95% de conformité (1 violation identifiée - voir ADR-002)
|
|
- Pattern Repository implémenté avec 13 repositories
|
|
|
|
### Alternatives considérées
|
|
|
|
**1. Clean Architecture (Uncle Bob)**
|
|
- Rejetée: Trop de couches (4-5) pour notre complexité
|
|
- Architecture hexagonale est plus simple et suffisante
|
|
|
|
**2. MVC traditionnel**
|
|
- Rejetée: Pas de séparation domaine/infrastructure
|
|
- Logique métier mélangée avec framework
|
|
|
|
**3. Feature Modules seuls (NestJS standard)**
|
|
- Rejetée: Domaine couplé à NestJS
|
|
- Difficile à tester et réutiliser
|
|
|
|
### Références
|
|
|
|
- [Hexagonal Architecture - Alistair Cockburn](https://alistair.cockburn.us/hexagonal-architecture/)
|
|
- [docs/architecture.md](./architecture.md)
|
|
- [CLAUDE.md](../CLAUDE.md)
|
|
|
|
---
|
|
|
|
## ADR-002: Suppression dépendances NestJS du domain layer
|
|
|
|
**Date**: 2025-12-22
|
|
**Status**: 🟡 Proposée
|
|
|
|
### Contexte
|
|
|
|
**Violation identifiée** lors de l'audit d'architecture:
|
|
- Le fichier `domain/services/booking.service.ts` importe `@nestjs/common`
|
|
- Utilise les decorators `@Injectable()` et `@Inject()`
|
|
- Utilise `NotFoundException` (exception NestJS, pas métier)
|
|
|
|
**Impact actuel**:
|
|
- Couplage du domain layer avec le framework NestJS
|
|
- Impossible de tester le service sans `TestingModule`
|
|
- Violation du principe d'inversion de dépendance
|
|
|
|
### Décision
|
|
|
|
**Supprimer toutes les dépendances NestJS du domain layer**:
|
|
|
|
1. Retirer `@Injectable()`, `@Inject()` de `BookingService`
|
|
2. Créer exception domaine `RateQuoteNotFoundException`
|
|
3. Adapter l'injection dans `bookings.module.ts`
|
|
4. Simplifier les tests unitaires
|
|
|
|
### Implémentation proposée
|
|
|
|
**Étape 1**: Refactoring du service domaine
|
|
|
|
```typescript
|
|
// domain/services/booking.service.ts
|
|
// AVANT
|
|
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
|
|
|
|
@Injectable()
|
|
export class BookingService {
|
|
constructor(
|
|
@Inject(BOOKING_REPOSITORY)
|
|
private readonly bookingRepository: BookingRepository,
|
|
) {}
|
|
}
|
|
|
|
// APRÈS
|
|
import { RateQuoteNotFoundException } from '../exceptions';
|
|
|
|
export class BookingService {
|
|
constructor(
|
|
private readonly bookingRepository: BookingRepository,
|
|
private readonly rateQuoteRepository: RateQuoteRepository,
|
|
) {}
|
|
}
|
|
```
|
|
|
|
**Étape 2**: Nouvelle exception domaine
|
|
|
|
```typescript
|
|
// domain/exceptions/rate-quote-not-found.exception.ts
|
|
export class RateQuoteNotFoundException extends Error {
|
|
constructor(public readonly rateQuoteId: string) {
|
|
super(`Rate quote with id ${rateQuoteId} not found`);
|
|
this.name = 'RateQuoteNotFoundException';
|
|
}
|
|
}
|
|
```
|
|
|
|
**Étape 3**: Adapter le module application
|
|
|
|
```typescript
|
|
// application/bookings/bookings.module.ts
|
|
@Module({
|
|
providers: [
|
|
{
|
|
provide: BookingService,
|
|
useFactory: (bookingRepo, rateQuoteRepo) => {
|
|
return new BookingService(bookingRepo, rateQuoteRepo);
|
|
},
|
|
inject: [BOOKING_REPOSITORY, RATE_QUOTE_REPOSITORY],
|
|
},
|
|
],
|
|
})
|
|
```
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- ✅ Conformité 100% architecture hexagonale
|
|
- ✅ Domain layer totalement indépendant du framework
|
|
- ✅ Tests plus simples et plus rapides
|
|
- ✅ Réutilisabilité du code domaine
|
|
|
|
**Négatives**:
|
|
- ⚠️ Légère verbosité dans la configuration des modules
|
|
- ⚠️ Besoin de mapper les exceptions (domaine → HTTP) dans application layer
|
|
|
|
**Risques**:
|
|
- ⚠️ **FAIBLE**: Risque de régression (tests doivent passer)
|
|
- ⚠️ **FAIBLE**: Impact sur les features dépendantes de BookingService
|
|
|
|
### Validation
|
|
|
|
**Critères d'acceptation**:
|
|
- [ ] ✅ Aucun import `@nestjs/*` dans `domain/`
|
|
- [ ] ✅ Tous les tests unitaires passent
|
|
- [ ] ✅ Tests d'intégration passent
|
|
- [ ] ✅ Tests E2E passent
|
|
- [ ] ✅ Pas de régression fonctionnelle
|
|
|
|
**Commande de vérification**:
|
|
```bash
|
|
# Vérifier l'absence d'imports NestJS dans domain
|
|
grep -r "from '@nestjs" apps/backend/src/domain/
|
|
# Résultat attendu: Aucun résultat
|
|
```
|
|
|
|
### Timeline
|
|
|
|
**Estimation**: 2-3 heures
|
|
- Refactoring: 1h
|
|
- Tests: 1h
|
|
- Review: 30min
|
|
|
|
### Références
|
|
|
|
- [docs/backend/cleanup-report.md](./backend/cleanup-report.md)
|
|
- [Hexagonal Architecture Principles](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/)
|
|
|
|
---
|
|
|
|
## ADR-003: Activation TypeScript strict mode frontend
|
|
|
|
**Date**: 2025-12-22
|
|
**Status**: 🟡 Proposée
|
|
|
|
### Contexte
|
|
|
|
**Problème identifié**:
|
|
- `tsconfig.json` a `"strict": false`
|
|
- Permet les erreurs de type silencieuses
|
|
- Risque de bugs runtime (`undefined is not a function`, `Cannot read property of null`)
|
|
- Code non type-safe difficile à maintenir
|
|
|
|
**Exemples de bugs non détectés sans strict mode**:
|
|
```typescript
|
|
// ❌ Accepté sans strict mode
|
|
let user: User;
|
|
console.log(user.name); // user peut être undefined
|
|
|
|
function getBooking(id?: string) {
|
|
return bookings.find(b => b.id === id); // id peut être undefined
|
|
}
|
|
```
|
|
|
|
### Décision
|
|
|
|
**Activer TypeScript strict mode** dans `tsconfig.json`:
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"strict": true
|
|
}
|
|
}
|
|
```
|
|
|
|
Ou activer les flags individuellement:
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"strictNullChecks": true,
|
|
"strictFunctionTypes": true,
|
|
"strictBindCallApply": true,
|
|
"strictPropertyInitialization": true,
|
|
"noImplicitAny": true,
|
|
"noImplicitThis": true,
|
|
"alwaysStrict": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- ✅ Détection des bugs au build time (pas runtime)
|
|
- ✅ Meilleure autocomplétion IDE (IntelliSense)
|
|
- ✅ Refactoring plus sûr
|
|
- ✅ Documentation implicite via les types
|
|
- ✅ Réduction des erreurs en production
|
|
|
|
**Négatives**:
|
|
- ⚠️ Corrections TypeScript nécessaires (estimation: 50-70% des fichiers)
|
|
- ⚠️ Apprentissage des patterns de null safety
|
|
- ⚠️ Code plus verbeux (guards, optional chaining)
|
|
|
|
**Risques**:
|
|
- ⚠️ **FAIBLE**: Pas de risque fonctionnel (corrections statiques)
|
|
- ⚠️ **MOYEN**: Temps de correction estimé à 2-3 jours
|
|
|
|
### Implémentation
|
|
|
|
**Phase 1: Activation progressive**
|
|
|
|
```bash
|
|
# 1. Activer strict mode
|
|
# tsconfig.json: "strict": true
|
|
|
|
# 2. Lister toutes les erreurs
|
|
npm run type-check 2>&1 | tee typescript-errors.log
|
|
|
|
# 3. Trier par fichier/type d'erreur
|
|
cat typescript-errors.log | sort | uniq -c
|
|
```
|
|
|
|
**Phase 2: Patterns de correction**
|
|
|
|
**Pattern 1**: Null checks
|
|
```typescript
|
|
// AVANT (strict: false)
|
|
function BookingDetails({ booking }) {
|
|
return <div>{booking.customerName}</div>;
|
|
}
|
|
|
|
// APRÈS (strict: true)
|
|
interface BookingDetailsProps {
|
|
booking: Booking | null;
|
|
}
|
|
|
|
function BookingDetails({ booking }: BookingDetailsProps) {
|
|
if (!booking) return <div>Loading...</div>;
|
|
return <div>{booking.customerName}</div>;
|
|
}
|
|
```
|
|
|
|
**Pattern 2**: Optional chaining
|
|
```typescript
|
|
// AVANT
|
|
const name = user.organization.name;
|
|
|
|
// APRÈS
|
|
const name = user?.organization?.name;
|
|
```
|
|
|
|
**Pattern 3**: Nullish coalescing
|
|
```typescript
|
|
// AVANT
|
|
const limit = params.limit || 20;
|
|
|
|
// APRÈS
|
|
const limit = params.limit ?? 20; // Gère correctement 0
|
|
```
|
|
|
|
**Phase 3: Validation**
|
|
|
|
```bash
|
|
# Vérifier qu'il n'y a plus d'erreurs TypeScript
|
|
npm run type-check
|
|
# Résultat attendu: Found 0 errors
|
|
|
|
# Build production
|
|
npm run build
|
|
# Résultat attendu: Build successful
|
|
```
|
|
|
|
### Timeline
|
|
|
|
**Estimation**: 2-3 jours
|
|
- Jour 1: Activer strict mode + lister erreurs
|
|
- Jour 2: Corriger 70% des erreurs
|
|
- Jour 3: Corriger les 30% restants + validation
|
|
|
|
### Alternatives considérées
|
|
|
|
**1. Garder strict: false**
|
|
- Rejetée: Accumulation de dette technique
|
|
- Risque de bugs en production
|
|
|
|
**2. Activation progressive flag par flag**
|
|
- Possible mais plus long
|
|
- Préférer activation directe (plus rapide)
|
|
|
|
**3. Migration vers Zod/io-ts pour runtime validation**
|
|
- Complémentaire (pas alternative)
|
|
- Peut être ajouté après strict mode
|
|
|
|
### Références
|
|
|
|
- [TypeScript Strict Mode Guide](https://www.typescriptlang.org/tsconfig#strict)
|
|
- [docs/frontend/cleanup-report.md](./frontend/cleanup-report.md)
|
|
|
|
---
|
|
|
|
## ADR-004: Standardisation pattern data fetching (React Query)
|
|
|
|
**Date**: 2025-12-22
|
|
**Status**: 🟡 Proposée
|
|
|
|
### Contexte
|
|
|
|
**Problème**: 3 patterns différents utilisés dans le frontend
|
|
|
|
**Pattern 1**: React Query + API client (✅ Recommandé)
|
|
```typescript
|
|
const { data } = useQuery({
|
|
queryKey: ['dashboard', 'kpis'],
|
|
queryFn: () => getDashboardKpis(),
|
|
});
|
|
```
|
|
|
|
**Pattern 2**: Custom hook avec fetch direct (❌ Problématique)
|
|
```typescript
|
|
const response = await fetch(`/api/v1/bookings/search`, {
|
|
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` },
|
|
});
|
|
```
|
|
|
|
**Pattern 3**: API client direct dans composant (⚠️ Acceptable)
|
|
```typescript
|
|
const { data } = useQuery({
|
|
queryKey: ['bookings'],
|
|
queryFn: () => listBookings({ page: 1, limit: 20 }),
|
|
});
|
|
```
|
|
|
|
**Problèmes identifiés**:
|
|
- Incohérence dans le codebase
|
|
- Token management en doublon
|
|
- Error handling différent partout
|
|
- Pas de cache centralisé
|
|
- Retry logic manquante
|
|
|
|
### Décision
|
|
|
|
**Standardiser sur Pattern 1**: **React Query + API client partout**
|
|
|
|
**Raisons**:
|
|
1. Token management centralisé (dans apiClient)
|
|
2. Error handling uniforme
|
|
3. Cache management automatique
|
|
4. Retry logic configurée
|
|
5. Type safety maximale
|
|
6. Optimistic updates possibles
|
|
|
|
### Implémentation
|
|
|
|
**Refactoring type**:
|
|
|
|
**AVANT** (useBookings.ts - Pattern 2):
|
|
```typescript
|
|
export function useBookings() {
|
|
const [bookings, setBookings] = useState([]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const searchBookings = async (filters) => {
|
|
setLoading(true);
|
|
const response = await fetch(`/api/v1/bookings/search`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
|
|
},
|
|
body: JSON.stringify(filters),
|
|
});
|
|
const data = await response.json();
|
|
setBookings(data);
|
|
setLoading(false);
|
|
};
|
|
|
|
return { bookings, loading, searchBookings };
|
|
}
|
|
```
|
|
|
|
**APRÈS** (useBookings.ts - Pattern 1):
|
|
```typescript
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { advancedSearchBookings } from '@/lib/api/bookings';
|
|
|
|
export function useBookings(filters: BookingFilters) {
|
|
return useQuery({
|
|
queryKey: ['bookings', 'search', filters],
|
|
queryFn: () => advancedSearchBookings(filters),
|
|
enabled: !!filters,
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
});
|
|
}
|
|
|
|
// Usage dans composant
|
|
const { data, isLoading, error } = useBookings(filters);
|
|
```
|
|
|
|
**Configuration centralisée**:
|
|
|
|
```typescript
|
|
// lib/providers/query-provider.tsx
|
|
export function QueryProvider({ children }) {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
staleTime: 60 * 1000, // 1 minute
|
|
retry: 3,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
mutations: {
|
|
retry: 1,
|
|
},
|
|
},
|
|
});
|
|
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
{children}
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- ✅ Code plus maintenable
|
|
- ✅ Moins de bugs (error handling centralisé)
|
|
- ✅ Meilleures performances (cache)
|
|
- ✅ Meilleure UX (loading states, retry)
|
|
- ✅ Dev experience améliorée (React Query DevTools)
|
|
|
|
**Négatives**:
|
|
- ⚠️ Refactoring nécessaire (estimation: 5-6 fichiers)
|
|
- ⚠️ Dépendance à React Query (mais déjà présent)
|
|
|
|
### Timeline
|
|
|
|
**Estimation**: 1 jour
|
|
- Refactoring hooks: 4h
|
|
- Tests: 2h
|
|
- Documentation: 1h
|
|
|
|
### Fichiers à modifier
|
|
|
|
1. `src/hooks/useBookings.ts` (supprimer fetch direct)
|
|
2. `src/hooks/useCsvRateSearch.ts` (vérifier pattern)
|
|
3. `src/hooks/useNotifications.ts` (vérifier pattern)
|
|
4. Tout autre hook custom utilisant fetch direct
|
|
|
|
### Validation
|
|
|
|
**Checklist**:
|
|
- [ ] ✅ Aucun `fetch()` direct dans hooks/composants
|
|
- [ ] ✅ Tous les calls API passent par `@/lib/api/*`
|
|
- [ ] ✅ Tous les hooks utilisent `useQuery` ou `useMutation`
|
|
- [ ] ✅ Token management unifié (via apiClient)
|
|
|
|
**Commande de vérification**:
|
|
```bash
|
|
# Chercher les fetch directs
|
|
grep -r "fetch(" apps/frontend/src/ apps/frontend/app/ | grep -v "api/client.ts"
|
|
# Résultat attendu: Aucun résultat (sauf dans client.ts)
|
|
```
|
|
|
|
### Références
|
|
|
|
- [React Query Best Practices](https://tkdodo.eu/blog/practical-react-query)
|
|
- [docs/frontend/cleanup-report.md](./frontend/cleanup-report.md)
|
|
|
|
---
|
|
|
|
## ADR-005: Migration pagination client-side vers serveur
|
|
|
|
**Date**: 2025-12-22
|
|
**Status**: 🟡 Proposée
|
|
|
|
### Contexte
|
|
|
|
**Problème actuel**:
|
|
```typescript
|
|
// app/dashboard/bookings/page.tsx (ligne 29)
|
|
listCsvBookings({ page: 1, limit: 1000 }) // ❌ Charge 1000 bookings !
|
|
|
|
// Puis pagination client-side
|
|
const currentBookings = filteredBookings.slice(startIndex, endIndex);
|
|
```
|
|
|
|
**Impact**:
|
|
- ⚠️ Transfert ~500KB-1MB de données
|
|
- ⚠️ Temps de chargement initial: 2-3 secondes
|
|
- ⚠️ Non scalable (impossible avec 10,000+ bookings)
|
|
- ⚠️ UX dégradée (loading long)
|
|
|
|
### Décision
|
|
|
|
**Implémenter pagination côté serveur** avec:
|
|
1. Requêtes paginées (20 items par page)
|
|
2. Filtres appliqués côté serveur
|
|
3. Cache React Query pour navigation rapide
|
|
4. Smooth transitions avec `keepPreviousData`
|
|
|
|
### Implémentation
|
|
|
|
**AVANT**:
|
|
```typescript
|
|
const { data: csvBookings } = useQuery({
|
|
queryKey: ['csv-bookings'],
|
|
queryFn: () => listCsvBookings({ page: 1, limit: 1000 }), // ❌ Tout charger
|
|
});
|
|
|
|
// Filtrage client-side
|
|
const filteredBookings = bookings.filter(/* ... */);
|
|
|
|
// Pagination client-side
|
|
const currentBookings = filteredBookings.slice(startIndex, endIndex);
|
|
```
|
|
|
|
**APRÈS**:
|
|
```typescript
|
|
const { data: csvBookings } = useQuery({
|
|
queryKey: ['csv-bookings', currentPage, filters],
|
|
queryFn: () => listCsvBookings({
|
|
page: currentPage,
|
|
limit: 20, // ✅ Pagination serveur
|
|
...filters, // ✅ Filtres serveur
|
|
}),
|
|
keepPreviousData: true, // ✅ Smooth transition entre pages
|
|
});
|
|
|
|
// Plus besoin de pagination client-side
|
|
const currentBookings = csvBookings?.data || [];
|
|
const totalPages = csvBookings?.meta.totalPages || 1;
|
|
```
|
|
|
|
**Vérifier API backend**:
|
|
```typescript
|
|
// Backend: apps/backend/src/application/controllers/csv-bookings.controller.ts
|
|
@Get()
|
|
async listCsvBookings(
|
|
@Query('page') page: number = 1,
|
|
@Query('limit') limit: number = 20,
|
|
@Query() filters: CsvBookingFiltersDto,
|
|
): Promise<PaginatedResponse<CsvBooking>> {
|
|
// ✅ L'API supporte déjà la pagination
|
|
return this.csvBookingService.findAll({ page, limit, filters });
|
|
}
|
|
```
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- ✅ Temps de chargement: 2s → 300ms
|
|
- ✅ Taille transfert: 500KB → 20KB
|
|
- ✅ Scalable: Supporte millions de records
|
|
- ✅ Meilleure UX: Chargement instantané
|
|
- ✅ Cache efficace: Une page = une requête
|
|
|
|
**Négatives**:
|
|
- ⚠️ Navigation entre pages = requête réseau
|
|
- Mitigé par `keepPreviousData` (pas de flash de loading)
|
|
- Mitigé par cache React Query (navigation arrière instantanée)
|
|
|
|
**Risques**:
|
|
- ⚠️ **FAIBLE**: Backend doit supporter les filtres serveur
|
|
|
|
### Validation
|
|
|
|
**Critères de performance**:
|
|
- [ ] ✅ Temps de chargement initial < 500ms
|
|
- [ ] ✅ Navigation entre pages < 300ms
|
|
- [ ] ✅ Taille transfert < 50KB par page
|
|
- [ ] ✅ Pas de flash de loading (keepPreviousData)
|
|
|
|
**Tests**:
|
|
```bash
|
|
# Test avec 10,000 bookings en base
|
|
# Avant: ~3s loading
|
|
# Après: ~300ms loading
|
|
```
|
|
|
|
### Timeline
|
|
|
|
**Estimation**: 2-3 heures
|
|
- Modification frontend: 1h
|
|
- Vérification backend: 30min
|
|
- Tests: 1h
|
|
|
|
### Fichiers à modifier
|
|
|
|
1. `app/dashboard/bookings/page.tsx` (refactoring pagination)
|
|
2. `lib/api/csv-bookings.ts` (vérifier support filtres serveur)
|
|
3. Potentiellement: `hooks/useBookings.ts` (si utilisé ailleurs)
|
|
|
|
### Références
|
|
|
|
- [React Query Pagination Guide](https://tanstack.com/query/latest/docs/react/guides/paginated-queries)
|
|
- [docs/frontend/cleanup-report.md](./frontend/cleanup-report.md)
|
|
|
|
---
|
|
|
|
## Template pour nouvelles décisions
|
|
|
|
```markdown
|
|
## ADR-XXX: [Titre de la décision]
|
|
|
|
**Date**: YYYY-MM-DD
|
|
**Status**: 🟡 Proposée / ✅ Acceptée / ❌ Rejetée / 🔄 Superseded
|
|
|
|
### Contexte
|
|
|
|
[Décrire le problème ou la situation qui nécessite une décision]
|
|
|
|
### Décision
|
|
|
|
[Décrire la décision prise et pourquoi]
|
|
|
|
### Implémentation
|
|
|
|
[Décrire comment la décision sera implémentée]
|
|
|
|
### Conséquences
|
|
|
|
**Positives**:
|
|
- [Liste des avantages]
|
|
|
|
**Négatives**:
|
|
- [Liste des inconvénients]
|
|
|
|
**Risques**:
|
|
- [Liste des risques]
|
|
|
|
### Alternatives considérées
|
|
|
|
[Décrire les autres options envisagées et pourquoi elles ont été rejetées]
|
|
|
|
### Validation
|
|
|
|
[Décrire comment valider que la décision est correctement implémentée]
|
|
|
|
### Timeline
|
|
|
|
[Estimation du temps nécessaire]
|
|
|
|
### Références
|
|
|
|
[Liens vers documentation, articles, ADRs liés]
|
|
```
|
|
|
|
---
|
|
|
|
**Fin du document**
|
|
|
|
Pour toute question ou proposition de nouvelle décision, contacter l'équipe architecture.
|