🏗️ Architecture technique
📋 Vue d'ensemble
L'architecture LIPAIX suit les principes de Clean Architecture avec des raccourcis pratiques pour faciliter la contribution et la maintenance.
🎯 Principes d'architecture
Clean Architecture (simplifiée)
Nous appliquons les principes fondamentaux de Clean Architecture :
- ✅ Séparation des responsabilités - Chaque couche a un rôle défini
- ✅ Inversion de dépendance - Les couches internes ne dépendent pas des externes
- ✅ Testabilité - Code structuré pour faciliter les tests
- ❌ Raccourcis - Simplifications pour faciliter la contribution
Structure en couches
┌─────────────────────────────────────┐
│ Presentation │ ← Composants React, pages Next.js
├─────────────────────────────────────┤
│ Use Cases │ ← Logique métier, commandes
├─────────────────────────────────────┤
│ Domain Layer │ ← Entités, interfaces, types
├─────────────────────────────────────┤
│ Infrastructure │ ← Base de données, APIs externes
└─────────────────────────────────────┘🏗️ Organisation du monorepo
Structure des dossiers
lipaix-web-v3/
├── 📁 apps/
│ ├── 🌐 web/ # Application Next.js + PayloadCMS
│ │ ├── 📁 src/
│ │ │ ├── 📁 app/ # App Router Next.js
│ │ │ ├── 📁 components/ # Composants React
│ │ │ ├── 📁 data/ # Couche infrastructure
│ │ │ └── 📁 core/ # Couche domaine
│ │ └── 📁 admin/ # Configuration PayloadCMS
│ └── 🤖 discord-bot/ # Bot Discord
│ ├── 📁 src/
│ │ ├── 📁 commands/ # Commandes slash
│ │ ├── 📁 services/ # Services métier
│ │ └── 📁 core/ # Logique principale
│ └── 📁 health/ # Health checks
├── 📁 shared/
│ └── 🔧 common/ # Code partagé
│ ├── 📁 src/
│ │ ├── 📁 core/ # Interfaces et types
│ │ ├── 📁 features/ # Logique métier partagée
│ │ └── 📁 utils/ # Utilitaires
│ └── 📁 package.json
└── 📁 docs/
└── 📚 vitepress/ # Documentation technique🔧 Couches techniques
1. Couche Présentation (Presentation Layer)
Responsabilité : Interface utilisateur et gestion des interactions
Technologies :
- React 18 - Composants et hooks
- Next.js 13+ - App Router et pages
- TailwindCSS - Styling utility-first
Exemple de composant :
// Composant de présentation
export function EventCard({ event }: { event: Event }) {
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h3 className="text-xl font-bold">{event.title}</h3>
<p className="text-gray-600">{event.description}</p>
<div className="mt-4">
<span className="text-sm text-blue-600">
{event.date.toLocaleDateString('fr-FR')}
</span>
</div>
</div>
);
}2. Couche Use Cases
Responsabilité : Logique métier et orchestration des opérations
Patterns utilisés :
- Command Pattern - Encapsulation des actions métier
- Query Pattern - Récupération de données
Exemple d'use case :
// Use case pour récupérer les prochains événements
export class GetNextFewEventsCommandUseCase {
constructor(
private eventsRepository: EventsRepository,
private cacheService: CacheService
) {}
async execute(count: number = 5): Promise<Event[]> {
// Vérifier le cache d'abord
const cached = await this.cacheService.get('next-events');
if (cached) return cached;
// Récupérer depuis le repository
const events = await this.eventsRepository.getNextFewEvents(count);
// Mettre en cache
await this.cacheService.set('next-events', events, 300); // 5 min
return events;
}
}3. Couche Domaine (Domain Layer)
Responsabilité : Entités métier, interfaces et types
Composants :
- Entités - Objets métier avec comportement
- Interfaces - Contrats entre couches
- Types - Définitions TypeScript
Exemple d'entité :
// Entité Event avec comportement métier
export class Event {
constructor(
public readonly id: string,
public readonly title: string,
public readonly description: string,
public readonly date: Date,
public readonly maxPlayers: number,
private currentPlayers: string[] = []
) {}
// Comportement métier
canJoin(playerId: string): boolean {
return !this.currentPlayers.includes(playerId) &&
this.currentPlayers.length < this.maxPlayers;
}
addPlayer(playerId: string): void {
if (this.canJoin(playerId)) {
this.currentPlayers.push(playerId);
}
}
get availableSpots(): number {
return this.maxPlayers - this.currentPlayers.length;
}
}4. Couche Infrastructure
Responsabilité : Accès aux données et services externes
Composants :
- Repositories - Accès aux données
- Services - Intégrations externes
- Adapters - Adaptation des données
Exemple de repository :
// Interface du repository
export interface EventsRepository {
getNextFewEvents(count: number): Promise<Event[]>;
getById(id: string): Promise<Event | null>;
save(event: Event): Promise<void>;
}
// Implémentation avec PayloadCMS
export class PayloadLocalAPIEventsRepository implements EventsRepository {
constructor(private payload: Payload) {}
async getNextFewEvents(count: number): Promise<Event[]> {
const response = await this.payload.find({
collection: 'shows',
where: {
date: {
greater_than: new Date().toISOString()
}
},
limit: count,
sort: 'date'
});
return response.docs.map(doc => this.mapToEvent(doc));
}
private mapToEvent(doc: any): Event {
return new Event(
doc.id,
doc.title,
doc.description,
new Date(doc.date),
doc.maxPlayers
);
}
}🔄 Injection de dépendances
Pattern GlobalRef (simplifié)
Pour éviter la complexité d'un container DI complet, nous utilisons un pattern simplifié :
// Service global avec lazy initialization
export class GlobalRef<T> {
private static instance: any;
static get<T>(factory: () => T): T {
if (!GlobalRef.instance) {
GlobalRef.instance = factory();
}
return GlobalRef.instance;
}
}
// Utilisation
export const eventsRepository = GlobalRef.get<EventsRepository>(
() => new PayloadLocalAPIEventsRepository(payload)
);🗄️ Gestion des données
Pattern Repository
Chaque entité métier a son repository :
EventsRepository- Gestion des événementsUsersRepository- Gestion des utilisateursAvailabilitiesRepository- Gestion des disponibilités
Cache et performance
- Redis - Cache des données fréquemment accédées
- Stratégie cache-first - Vérifier le cache avant la base
- TTL configurable - Expiration automatique des données
🔐 Sécurité et accès
Contrôle d'accès PayloadCMS
// Configuration des rôles et permissions
export const Show: CollectionConfig = {
access: {
read: () => true, // Lecture publique
create: ({ req: { user } }) => {
return user?.role === 'admin';
},
update: ({ req: { user } }) => {
return user?.role === 'admin';
}
}
};Validation des données
- Zod - Validation des schémas
- PayloadCMS - Validation côté serveur
- React Hook Form - Validation côté client
🧪 Testabilité
Structure des tests
__tests__/
├── 📁 unit/ # Tests unitaires
├── 📁 integration/ # Tests d'intégration
└── 📁 e2e/ # Tests end-to-endMocks et stubs
// Mock du repository pour les tests
export class MockEventsRepository implements EventsRepository {
private events: Event[] = [];
async getNextFewEvents(count: number): Promise<Event[]> {
return this.events.slice(0, count);
}
addEvent(event: Event): void {
this.events.push(event);
}
}🚀 Déploiement et infrastructure
Services Railway
- Web App - Next.js + PayloadCMS
- Discord Bot - Node.js standalone
- Database - PostgreSQL
- Cache - Redis
- Documentation - VitePress
Variables d'environnement
# Base de données
DATABASE_URL=postgresql://...
REDIS_URL=redis://...
# Discord
DISCORD_TOKEN=...
DISCORD_CLIENT_ID=...
# PayloadCMS
PAYLOAD_SECRET=...📊 Monitoring et observabilité
Health checks
/api/health- Endpoint de santé- Sentry - Monitoring des erreurs
- Logs structurés - Traçabilité des opérations
Métriques
- Temps de réponse des APIs
- Taux d'erreur par endpoint
- Utilisation des ressources (CPU, mémoire)
🔄 Évolution de l'architecture
Principes de refactoring
- Extract Method - Extraire la logique complexe
- Extract Class - Séparer les responsabilités
- Introduce Interface - Découpler les couches
- Move Method - Placer le code au bon endroit
Signaux de refactoring
- Classes trop grandes (>200 lignes)
- Méthodes trop longues (>20 lignes)
- Duplication de code
- Violation du principe de responsabilité unique
🎯 Bonnes pratiques
Do's
- ✅ Séparer les responsabilités - Chaque classe a un rôle unique
- ✅ Utiliser les interfaces - Découpler les implémentations
- ✅ Tester les use cases - Logique métier testable
- ✅ Valider les données - Entrées et sorties validées
Don'ts
- ❌ Mélanger les couches - Présentation dans le domaine
- ❌ Créer des dépendances circulaires
- ❌ Ignorer la gestion d'erreurs
- ❌ Oublier la validation des données
🚀 Prochaines étapes
- Frontend - Découvrir React et Next.js
- Backend - Explorer PayloadCMS
- Bot Discord - Discord.js et intégrations
- Déploiement - Railway et workflows
Cette architecture évolue avec le projet. N'hésitez pas à proposer des améliorations ! 🚀
