970 lines
32 KiB
Markdown
970 lines
32 KiB
Markdown
---
|
||
stepsCompleted: ['step-01-init', 'step-02-context', 'step-03-starter', 'step-04-decisions', 'step-05-patterns', 'step-06-structure']
|
||
inputDocuments:
|
||
- prd.md
|
||
workflowType: 'architecture'
|
||
project_name: 'chartbastan'
|
||
user_name: 'Ramez'
|
||
date: '2026-01-15T21:00:00.000Z'
|
||
lastStep: 8
|
||
status: 'complete'
|
||
completedAt: '2026-01-15T22:00:00.000Z'
|
||
---
|
||
|
||
# Architecture Decision Document
|
||
|
||
_This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together._
|
||
|
||
## Project Context Analysis
|
||
|
||
### Requirements Overview
|
||
|
||
**Functional Requirements:**
|
||
|
||
Le projet chartbastan est une application web de prédiction de résultats de football basée sur l'analyse de l'énergie collective des supporters via les réseaux sociaux. Les exigences fonctionnelles principales incluent :
|
||
|
||
- **Collecte de données multi-sources** : Scraping Twitter (60%), Reddit (25%), RSS (15%) avec pondération intelligente
|
||
- **Analyse de sentiment en temps réel** : Utilisation de VADER/textblob pour analyser 1000+ tweets en < 1 seconde
|
||
- **Calcul d'énergie collective** : Formule Score = (Positif - Négatif) × Volume × Viralité avec pondération temporelle
|
||
- **Système de backtesting** : Validation sur 100+ matchs historiques avec seuil de précision ≥ 60%
|
||
- **Dashboard temps réel** : Visualisations interactives (D3.js) avec Confidence Meter (0-100%)
|
||
- **Gamification** : Système de classement, badges, parrainage (invite 3 amis = 1 mois premium gratuit)
|
||
- **Modèle freemium** : Version gratuite (1-2 prédictions/jour) + Premium (19.99€/mois) avec métriques avancées
|
||
- **API publique** : Documentation et accès pour développeurs tiers (Phase 2+)
|
||
- **Notifications push** : Alertes pour changements majeurs d'énergie collective
|
||
|
||
**Non-Functional Requirements:**
|
||
|
||
Les NFRs critiques qui façonneront l'architecture :
|
||
|
||
- **Performance** : Dashboard mis à jour en < 3 secondes, analyse de 1000+ tweets en < 1 seconde
|
||
- **Disponibilité** : Uptime > 99.5% pour l'API FastAPI principale
|
||
- **Scalabilité** : Système de queue asynchrone (RabbitMQ) pour gérer les pics de charge
|
||
- **Précision** : Taux de précision ≥ 60% (seuil de validation, < 55% = révision requise)
|
||
- **Rate limiting** : Gestion robuste des limites API Twitter (1000 req/heure gratuit)
|
||
- **Qualité frontend** : Next.js 16 responsive avec Lighthouse score > 90
|
||
- **Fiabilité** : Scraping Twitter stable sur 24h sans interruption
|
||
- **Transparence** : Confidence Meter précis à ±5% du taux de réussite réel
|
||
|
||
**Scale & Complexity:**
|
||
|
||
- **Primary domain** : Full-stack web application (Next.js 16 frontend + FastAPI backend)
|
||
- **Complexity level** : High (classification PRD)
|
||
- **Estimated architectural components** : ~11 composants majeurs
|
||
- Service de scraping multi-sources
|
||
- Service d'analyse de sentiment
|
||
- Service de calcul d'énergie
|
||
- API backend (FastAPI)
|
||
- API frontend (Next.js API routes)
|
||
- Dashboard frontend (Next.js + D3.js)
|
||
- Système de queue (RabbitMQ)
|
||
- Base de données (SQLite Phase 1)
|
||
- Service de notifications
|
||
- Système d'authentification/autorisation
|
||
- Service de backtesting
|
||
|
||
### Technical Constraints & Dependencies
|
||
|
||
**Contraintes budgétaires Phase 1 :**
|
||
- Budget minimal : 10-20€/mois
|
||
- SQLite (gratuit) suffisant pour Phase 1
|
||
- API Twitter gratuite (1000 req/heure)
|
||
- Pas d'app mobile complexe (web responsive suffisant)
|
||
|
||
**Stack technique imposé :**
|
||
- Frontend : Next.js 16 + Tailwind 4 + shadcn/ui
|
||
- Backend primaire : Python FastAPI (analytics, ML, sentiment analysis)
|
||
- Backend secondaire : Node.js (Next.js API routes pour API légère, authentification)
|
||
- Base de données : SQLite (Phase 1) → migration future prévue
|
||
- Queue : RabbitMQ pour traitement asynchrone
|
||
|
||
**Dépendances externes critiques :**
|
||
- Twitter API (rate limit : 1000 req/heure)
|
||
- Reddit API (généreuse, pas de rate limiting strict)
|
||
- RSS Feeds (diverses sources)
|
||
- Mailchimp (gratuit pour capture emails Phase 1)
|
||
|
||
**Contraintes de domaine :**
|
||
- Focus unique sur Football pour Phase 1
|
||
- Domaine gambling/betting (implications compliance potentielles)
|
||
- Projet greenfield (nouveau développement)
|
||
|
||
### Cross-Cutting Concerns Identified
|
||
|
||
**Gestion des API externes :**
|
||
- Rate limiting et priorisation dynamique (matchs VIP vs autres)
|
||
- Gestion des pannes (modes dégradés : Reddit + RSS uniquement)
|
||
- Monitoring et alertes prédictives (> 90% utilisation)
|
||
- Optimisation intelligente (scraper tweets avec engagement élevé)
|
||
|
||
**Traitement asynchrone :**
|
||
- Queue RabbitMQ pour découpler scraping et analyse
|
||
- Gestion des pics de charge (événements majeurs)
|
||
- Traitement batch pour backtesting
|
||
|
||
**Temps réel :**
|
||
- Dashboard avec mises à jour en temps réel
|
||
- Notifications push pour changements majeurs
|
||
- Visualisations dynamiques (D3.js)
|
||
|
||
**Multi-tenant :**
|
||
- Différenciation gratuit/premium (features, SLA, rate limiting)
|
||
- Système d'authentification/autorisation
|
||
- Métriques et analytics différenciés
|
||
|
||
**Compliance & Transparence :**
|
||
- Domaine gambling/betting (réglementations potentielles)
|
||
- Transparence algorithmique (explication des prédictions)
|
||
- Documentation publique des résultats (Medium, Reddit)
|
||
|
||
## Starter Template Evaluation
|
||
|
||
### Primary Technology Domain
|
||
|
||
Full-stack web application basé sur l'analyse du contexte projet et des exigences.
|
||
|
||
### Starter Options Considered
|
||
|
||
**Option 1 : create-next-app (officiel Next.js)**
|
||
- Starter officiel Next.js 16 avec support App Router, TypeScript, Tailwind CSS, ESLint/Biome
|
||
- Turbopack activé par défaut pour un bundling rapide
|
||
- Structure modulaire avec `src/` directory
|
||
- Intégration shadcn/ui via CLI séparé
|
||
|
||
**Option 2 : shadcn/ui CLI (canary pour Tailwind v4)**
|
||
- Compatible Tailwind v4 + React 19
|
||
- Intégration native avec Next.js 16
|
||
- Style "new-york" par défaut
|
||
- Composants copiés dans le projet (pas de dépendance externe)
|
||
|
||
### Selected Starter: create-next-app + shadcn/ui
|
||
|
||
**Rationale for Selection:**
|
||
|
||
Approche en deux étapes recommandée :
|
||
1. `create-next-app` pour établir la base Next.js 16 avec toutes les configurations essentielles
|
||
2. `shadcn@canary` pour intégrer l'UI avec Tailwind v4, aligné avec les préférences techniques du PRD
|
||
|
||
Cette approche offre :
|
||
- Base solide et officielle avec Next.js 16
|
||
- Flexibilité pour intégrer shadcn/ui avec Tailwind v4
|
||
- Alignement avec les contraintes techniques du projet (Next.js 16 + Tailwind 4 + shadcn/ui)
|
||
|
||
**Initialization Command:**
|
||
|
||
```bash
|
||
# 1. Créer le projet Next.js 16 avec TypeScript, Tailwind, ESLint
|
||
npx create-next-app@latest chartbastan --typescript --eslint --tailwind --app --src-dir --turbopack
|
||
|
||
cd chartbastan
|
||
|
||
# 2. Initialiser shadcn/ui avec Tailwind v4 (canary pour v4)
|
||
npx shadcn@canary init
|
||
```
|
||
|
||
**Architectural Decisions Provided by Starter:**
|
||
|
||
**Language & Runtime:**
|
||
- TypeScript avec configuration stricte activée
|
||
- Node.js 20.9+ requis (minimum pour Next.js 16)
|
||
- App Router (Next.js 16) par défaut avec Server Components
|
||
|
||
**Styling Solution:**
|
||
- Tailwind CSS v4 configuré via shadcn CLI
|
||
- shadcn/ui avec style "new-york" par défaut
|
||
- CSS Variables pour theming (dark mode support)
|
||
- Configuration via `components.json`
|
||
|
||
**Build Tooling:**
|
||
- Turbopack activé par défaut (bundler rapide de Next.js)
|
||
- Optimisations automatiques (Server Components, code splitting par route)
|
||
- Alias `@/*` configuré pour imports simplifiés
|
||
|
||
**Code Organization:**
|
||
- Structure `src/` directory pour séparation claire
|
||
- App Router avec `/app` directory pour routing
|
||
- Composants UI dans `components/ui/` (copiés localement)
|
||
- Utilitaires dans `lib/utils.ts` (helper `cn()` pour classnames)
|
||
|
||
**Development Experience:**
|
||
- Hot reloading avec Turbopack pour développement rapide
|
||
- ESLint configuré avec règles Next.js
|
||
- TypeScript strict mode pour sécurité de types
|
||
- Support Server Components par défaut (réduction bundle client)
|
||
|
||
**Note:** L'initialisation du projet avec cette commande devrait être la première story d'implémentation.
|
||
|
||
## Core Architectural Decisions
|
||
|
||
### Decision Priority Analysis
|
||
|
||
**Critical Decisions (Block Implementation):**
|
||
- Architecture des données (ORM, migrations)
|
||
- Authentification et autorisation
|
||
- Patterns API et communication
|
||
- Gestion d'état frontend
|
||
- Infrastructure de déploiement
|
||
|
||
**Important Decisions (Shape Architecture):**
|
||
- Stratégie de cache
|
||
- Monitoring et logging
|
||
- Gestion d'erreurs standardisée
|
||
- Rate limiting
|
||
|
||
**Deferred Decisions (Post-MVP):**
|
||
- Migration SQLite → PostgreSQL/MySQL (Phase 2+)
|
||
- Optimisations avancées de performance
|
||
- Multi-région deployment
|
||
- CDN et edge caching
|
||
|
||
### Data Architecture
|
||
|
||
**ORM Selection:**
|
||
|
||
**FastAPI Backend:**
|
||
- **Choice:** SQLAlchemy 2.0.45 (latest stable)
|
||
- **Rationale:**
|
||
- Mature, bien intégré avec FastAPI
|
||
- Support async natif pour performance
|
||
- Compatible avec Pydantic pour validation
|
||
- Migration facile vers PostgreSQL/MySQL en Phase 2
|
||
- **Migration Tool:** Alembic pour migrations de schéma
|
||
- **Affects:** Service FastAPI (analytics, ML, sentiment analysis)
|
||
|
||
**Next.js API Routes:**
|
||
- **Choice:** Drizzle ORM v0.44.7 (stable) avec `better-sqlite3`
|
||
- **Rationale:**
|
||
- TypeScript-first, excellent DX
|
||
- Léger et performant
|
||
- Support SQLite natif avec `better-sqlite3`
|
||
- Migrations via Drizzle Kit
|
||
- Compatible avec Server Components Next.js
|
||
- **Migration Tool:** Drizzle Kit pour migrations
|
||
- **Affects:** Next.js API routes (authentification, API légère)
|
||
|
||
**Data Modeling Approach:**
|
||
- **Schema Design:** Modélisation relationnelle classique avec normalisation
|
||
- **Validation:**
|
||
- FastAPI: Pydantic models pour validation des entrées/sorties
|
||
- Next.js: Zod pour validation côté API routes (cohérence TypeScript)
|
||
- **Migration Strategy:**
|
||
- Migrations versionnées (Alembic + Drizzle Kit)
|
||
- Scripts de migration pour Phase 1 → Phase 2 (SQLite → PostgreSQL)
|
||
|
||
**Caching Strategy:**
|
||
- **Phase 1:** Pas de cache distribué (SQLite suffisant)
|
||
- **Phase 2+:** Redis pour cache de prédictions et données fréquemment accédées
|
||
- **Frontend:** React Query (TanStack Query) pour cache côté client avec invalidation intelligente
|
||
|
||
### Authentication & Security
|
||
|
||
**Authentication Method:**
|
||
- **Choice:** Better Auth v1.4.4 (compatible Next.js 16)
|
||
- **Rationale:**
|
||
- Compatible officiellement avec Next.js 16 (contrairement à NextAuth.js v4)
|
||
- Support stateless auth (JWT)
|
||
- Migration path depuis NextAuth.js documentée
|
||
- Support OAuth (Google, Twitter) pour Phase 2
|
||
- **Version:** v1.4.4 (production-ready)
|
||
- **Affects:** Next.js API routes, système d'authentification
|
||
|
||
**Authorization Patterns:**
|
||
- **Role-Based Access Control (RBAC):** Gratuit vs Premium
|
||
- **Permissions:**
|
||
- Gratuit: 1-2 prédictions/jour, accès limité
|
||
- Premium: Accès illimité, métriques avancées, API prioritaire
|
||
- **Implementation:** Middleware Next.js pour protection des routes API
|
||
|
||
**Security Middleware:**
|
||
- **FastAPI:**
|
||
- CORS configuré pour domaines autorisés
|
||
- Rate limiting via `slowapi` ou middleware custom
|
||
- Validation Pydantic pour toutes les entrées
|
||
- **Next.js:**
|
||
- CSRF protection via middleware
|
||
- Secure cookies (HttpOnly, Secure, SameSite)
|
||
- Headers de sécurité (CSP, X-Frame-Options, etc.)
|
||
|
||
**Data Encryption:**
|
||
- **At Rest:** SQLite avec chiffrement optionnel (Phase 2+)
|
||
- **In Transit:** HTTPS obligatoire (TLS 1.3)
|
||
- **Secrets:** Variables d'environnement, jamais en code
|
||
- **Passwords:** Hashing avec bcrypt (via Better Auth)
|
||
|
||
### API & Communication Patterns
|
||
|
||
**API Design Pattern:**
|
||
- **Choice:** RESTful API avec OpenAPI 3.1
|
||
- **Rationale:**
|
||
- Standard, bien compris
|
||
- Documentation automatique avec FastAPI
|
||
- Compatible avec génération de clients TypeScript
|
||
- **Version:** OpenAPI 3.1 (supporté nativement par FastAPI 0.128.0+)
|
||
|
||
**API Documentation:**
|
||
- **FastAPI:**
|
||
- Swagger UI intégré (`/docs`)
|
||
- Redoc alternatif (`/redoc`)
|
||
- OpenAPI schema exportable (`/openapi.json`)
|
||
- Pydantic models pour schémas automatiques
|
||
- **Best Practices:**
|
||
- Docstrings Markdown pour chaque endpoint
|
||
- Tags pour groupement logique
|
||
- Exemples dans les schémas
|
||
- Documentation des erreurs (400, 401, 404, 500)
|
||
- **Production:** Désactiver `/docs` et `/redoc` ou les protéger par authentification
|
||
|
||
**Error Handling Standards:**
|
||
- **Format Standardisé:**
|
||
```json
|
||
{
|
||
"error": {
|
||
"code": "ERROR_CODE",
|
||
"message": "Human-readable message",
|
||
"details": {}
|
||
}
|
||
}
|
||
```
|
||
- **HTTP Status Codes:**
|
||
- 200: Success
|
||
- 400: Bad Request (validation)
|
||
- 401: Unauthorized
|
||
- 403: Forbidden (premium required)
|
||
- 404: Not Found
|
||
- 429: Too Many Requests (rate limit)
|
||
- 500: Internal Server Error
|
||
- **Logging:** Tous les erreurs 4xx/5xx loggés avec contexte
|
||
|
||
**Rate Limiting Strategy:**
|
||
- **FastAPI:**
|
||
- `slowapi` ou middleware custom
|
||
- Limites différenciées: Gratuit (10 req/min), Premium (100 req/min)
|
||
- Headers `X-RateLimit-*` pour transparence
|
||
- **Next.js API Routes:**
|
||
- Rate limiting par utilisateur (Better Auth)
|
||
- Protection contre abuse
|
||
- **External APIs:**
|
||
- Gestion proactive des limites Twitter (1000 req/heure)
|
||
- Priorisation dynamique (matchs VIP)
|
||
- Modes dégradés (Reddit + RSS si Twitter down)
|
||
|
||
**Communication Between Services:**
|
||
- **FastAPI ↔ Next.js:**
|
||
- HTTP REST pour communication synchrone
|
||
- RabbitMQ pour communication asynchrone (scraping, analyse)
|
||
- **Message Queue:** RabbitMQ pour découplage scraping/analyse
|
||
- **Service Discovery:** URLs configurées via variables d'environnement (Phase 1)
|
||
|
||
### Frontend Architecture
|
||
|
||
**State Management:**
|
||
- **Choice:** Zustand v5.0.9 + React Query (TanStack Query)
|
||
- **Rationale:**
|
||
- Zustand: Léger, pas de provider, SSR-safe avec `unstable_ssrSafe`
|
||
- React Query: Cache intelligent, synchronisation serveur, invalidation automatique
|
||
- Complémentaire: Zustand pour état UI local, React Query pour données serveur
|
||
- **Version:** Zustand v5.0.9 (latest stable)
|
||
- **Affects:** Composants React, gestion d'état global
|
||
|
||
**Component Architecture:**
|
||
- **Pattern:** Feature-based organization avec co-location
|
||
- **Structure:**
|
||
```
|
||
src/
|
||
app/ # Routes (App Router)
|
||
components/
|
||
ui/ # shadcn/ui components
|
||
features/ # Feature-specific components
|
||
predictions/
|
||
dashboard/
|
||
lib/ # Utilities, hooks
|
||
stores/ # Zustand stores
|
||
```
|
||
- **Server Components:** Par défaut, `use client` uniquement si nécessaire
|
||
|
||
**Routing Strategy:**
|
||
- **Choice:** Next.js App Router (déjà décidé par starter)
|
||
- **File-based routing** avec layouts et loading states
|
||
- **Protected Routes:** Middleware pour authentification
|
||
|
||
**Performance Optimization:**
|
||
- **Code Splitting:** Automatique par route (App Router)
|
||
- **Image Optimization:** Next.js Image component
|
||
- **Bundle Analysis:** `@next/bundle-analyzer` pour monitoring
|
||
- **Lazy Loading:** Dynamic imports pour composants lourds (D3.js)
|
||
- **Server Components:** Maximiser pour réduire bundle client
|
||
|
||
### Infrastructure & Deployment
|
||
|
||
**Hosting Strategy:**
|
||
- **Frontend (Next.js):**
|
||
- **Phase 1:** Vercel (gratuit, optimisé Next.js, déploiement automatique)
|
||
- **Rationale:** Zero-config, excellent DX, CDN intégré
|
||
- **Backend (FastAPI):**
|
||
- **Phase 1:** Railway ou Render (budget 10-20€/mois)
|
||
- **Rationale:** Support Python, déploiement simple, scaling facile
|
||
- **Database:**
|
||
- **Phase 1:** SQLite (fichier local, backup manuel)
|
||
- **Phase 2+:** PostgreSQL sur Railway/Render ou Supabase
|
||
|
||
**CI/CD Pipeline:**
|
||
- **Choice:** GitHub Actions
|
||
- **Workflow:**
|
||
- Lint + Type check sur PR
|
||
- Tests unitaires (si implémentés Phase 2+)
|
||
- Build et déploiement automatique sur merge main
|
||
- **Environments:**
|
||
- `development`: Local
|
||
- `staging`: Vercel Preview (auto)
|
||
- `production`: Vercel Production + Railway/Render
|
||
|
||
**Environment Configuration:**
|
||
- **Management:** `.env.local` (dev), `.env.production` (prod)
|
||
- **Secrets:** Variables d'environnement dans Vercel/Railway
|
||
- **Validation:** Zod pour validation des env vars au runtime
|
||
|
||
**Monitoring and Logging:**
|
||
- **Phase 1:**
|
||
- **Logging:** Console logs structurés (JSON)
|
||
- **Errors:** Sentry (plan gratuit) pour tracking erreurs
|
||
- **Uptime:** UptimeRobot (gratuit) pour monitoring basique
|
||
- **Phase 2+:**
|
||
- **APM:** Datadog ou New Relic (si budget)
|
||
- **Analytics:** Vercel Analytics intégré
|
||
- **Performance:** Web Vitals monitoring
|
||
|
||
**Scaling Strategy:**
|
||
- **Phase 1:** Vertical scaling (upgrade instance)
|
||
- **Phase 2+:**
|
||
- Horizontal scaling (multiple instances FastAPI)
|
||
- Load balancer (Railway/Render)
|
||
- Database: Migration PostgreSQL avec connection pooling
|
||
- CDN: Vercel Edge Network (automatique)
|
||
|
||
### Decision Impact Analysis
|
||
|
||
**Implementation Sequence:**
|
||
|
||
1. **Foundation:**
|
||
- Initialiser Next.js 16 avec create-next-app
|
||
- Configurer shadcn/ui avec Tailwind v4
|
||
- Setup FastAPI avec SQLAlchemy
|
||
- Configurer SQLite (FastAPI + Next.js)
|
||
|
||
2. **Core Services:**
|
||
- Implémenter Better Auth (authentification)
|
||
- Setup Drizzle ORM (Next.js)
|
||
- Setup SQLAlchemy + Alembic (FastAPI)
|
||
- Configurer RabbitMQ pour queue
|
||
|
||
3. **API Layer:**
|
||
- Définir schémas OpenAPI (FastAPI)
|
||
- Implémenter endpoints REST
|
||
- Setup rate limiting
|
||
- Documentation API
|
||
|
||
4. **Frontend:**
|
||
- Configurer Zustand + React Query
|
||
- Implémenter composants UI (shadcn)
|
||
- Setup routing et protection
|
||
- Intégration D3.js pour visualisations
|
||
|
||
5. **Infrastructure:**
|
||
- Setup Vercel (frontend)
|
||
- Setup Railway/Render (backend)
|
||
- Configurer CI/CD (GitHub Actions)
|
||
- Setup monitoring (Sentry, UptimeRobot)
|
||
|
||
**Cross-Component Dependencies:**
|
||
|
||
- **Authentication → API:** Better Auth protège toutes les routes API
|
||
- **Database → Services:** SQLAlchemy (FastAPI) et Drizzle (Next.js) partagent le même SQLite (Phase 1)
|
||
- **Queue → Scraping:** RabbitMQ découple scraping Twitter/Reddit de l'analyse
|
||
- **Frontend → Backend:** React Query synchronise état avec API FastAPI
|
||
- **Monitoring → All:** Sentry track erreurs dans tous les services
|
||
|
||
## Implementation Patterns & Consistency Rules
|
||
|
||
### Pattern Categories Defined
|
||
|
||
**Critical Conflict Points Identified:**
|
||
|
||
15+ zones où les agents AI pourraient faire des choix différents, nécessitant des patterns stricts pour garantir la cohérence.
|
||
|
||
### Naming Patterns
|
||
|
||
**Database Naming Conventions:**
|
||
|
||
**FastAPI (SQLAlchemy):**
|
||
- **Tables:** `snake_case`, pluriel : `users`, `predictions`, `matches`, `energy_scores`
|
||
- **Columns:** `snake_case` : `user_id`, `created_at`, `is_premium`, `energy_score`
|
||
- **Foreign Keys:** `{table}_id` : `user_id`, `match_id`, `prediction_id`
|
||
- **Indexes:** `idx_{table}_{column}` : `idx_users_email`, `idx_predictions_match_id`
|
||
- **Constraints:** `{table}_{column}_{constraint}` : `users_email_unique`, `predictions_user_id_fk`
|
||
|
||
**Next.js (Drizzle ORM):**
|
||
- **Tables:** Même convention que SQLAlchemy (`snake_case`, pluriel)
|
||
- **Schema Definition:** Utiliser `snake_case` dans les schémas Drizzle pour correspondre à SQLite
|
||
- **TypeScript Types:** Générer types TypeScript avec `snake_case` → `camelCase` via Drizzle
|
||
|
||
**Rationale:** Cohérence entre les deux ORMs partageant la même base SQLite. SQLAlchemy utilise traditionnellement `snake_case`, donc Drizzle doit suivre.
|
||
|
||
**API Naming Conventions:**
|
||
|
||
**REST Endpoints:**
|
||
- **Base URLs:** Pluriel, `snake_case` : `/api/v1/users`, `/api/v1/predictions`, `/api/v1/matches`
|
||
- **Route Parameters:** `{id}` format : `/api/v1/users/{id}`, `/api/v1/matches/{match_id}`
|
||
- **Query Parameters:** `snake_case` : `?user_id=123&limit=10&offset=0`
|
||
- **Nested Resources:** `/api/v1/users/{user_id}/predictions`
|
||
- **Actions:** `/api/v1/predictions/{id}/validate`, `/api/v1/matches/{id}/analyze`
|
||
|
||
**Headers:**
|
||
- **Custom Headers:** `X-` prefix : `X-API-Key`, `X-User-Id`, `X-RateLimit-Remaining`
|
||
- **Standard Headers:** Respecter conventions HTTP standard
|
||
|
||
**Code Naming Conventions:**
|
||
|
||
**Python (FastAPI):**
|
||
- **Files:** `snake_case.py` : `user_service.py`, `prediction_service.py`, `energy_calculator.py`
|
||
- **Classes:** `PascalCase` : `UserService`, `PredictionService`, `EnergyCalculator`
|
||
- **Functions:** `snake_case` : `get_user_by_id()`, `calculate_energy_score()`, `create_prediction()`
|
||
- **Variables:** `snake_case` : `user_id`, `energy_score`, `is_premium`
|
||
- **Constants:** `UPPER_SNAKE_CASE` : `MAX_PREDICTIONS_FREE`, `API_RATE_LIMIT`
|
||
|
||
**TypeScript/JavaScript (Next.js):**
|
||
- **Files:** `kebab-case.tsx` ou `PascalCase.tsx` pour composants : `user-card.tsx`, `UserCard.tsx`, `prediction-service.ts`
|
||
- **Components:** `PascalCase` : `UserCard`, `PredictionDashboard`, `EnergyMeter`
|
||
- **Functions:** `camelCase` : `getUserById()`, `calculateEnergyScore()`, `createPrediction()`
|
||
- **Variables:** `camelCase` : `userId`, `energyScore`, `isPremium`
|
||
- **Constants:** `UPPER_SNAKE_CASE` : `MAX_PREDICTIONS_FREE`, `API_RATE_LIMIT`
|
||
- **Types/Interfaces:** `PascalCase` : `User`, `Prediction`, `EnergyScore`
|
||
|
||
**Hooks:** `camelCase` avec préfixe `use` : `useUser()`, `usePredictions()`, `useEnergyScore()`
|
||
|
||
### Structure Patterns
|
||
|
||
**Project Organization:**
|
||
|
||
**FastAPI Backend Structure:**
|
||
```
|
||
backend/
|
||
├── app/
|
||
│ ├── __init__.py
|
||
│ ├── main.py # FastAPI app entry
|
||
│ ├── config.py # Configuration
|
||
│ ├── database.py # SQLAlchemy setup
|
||
│ ├── models/ # SQLAlchemy models
|
||
│ │ ├── __init__.py
|
||
│ │ ├── user.py
|
||
│ │ ├── prediction.py
|
||
│ │ └── match.py
|
||
│ ├── schemas/ # Pydantic schemas
|
||
│ │ ├── __init__.py
|
||
│ │ ├── user.py
|
||
│ │ └── prediction.py
|
||
│ ├── api/ # API routes
|
||
│ │ ├── __init__.py
|
||
│ │ ├── v1/
|
||
│ │ │ ├── __init__.py
|
||
│ │ │ ├── users.py
|
||
│ │ │ └── predictions.py
|
||
│ ├── services/ # Business logic
|
||
│ │ ├── __init__.py
|
||
│ │ ├── user_service.py
|
||
│ │ ├── prediction_service.py
|
||
│ │ └── energy_calculator.py
|
||
│ ├── repositories/ # Data access layer
|
||
│ │ ├── __init__.py
|
||
│ │ ├── user_repository.py
|
||
│ │ └── prediction_repository.py
|
||
│ └── utils/ # Utilities
|
||
│ ├── __init__.py
|
||
│ ├── logger.py
|
||
│ └── validators.py
|
||
├── alembic/ # Migrations
|
||
│ ├── versions/
|
||
│ └── env.py
|
||
├── tests/ # Tests (co-located or separate)
|
||
│ ├── __init__.py
|
||
│ ├── test_users.py
|
||
│ └── test_predictions.py
|
||
├── requirements.txt
|
||
└── .env
|
||
```
|
||
|
||
**Next.js Frontend Structure:**
|
||
```
|
||
frontend/
|
||
├── src/
|
||
│ ├── app/ # App Router routes
|
||
│ │ ├── layout.tsx
|
||
│ │ ├── page.tsx
|
||
│ │ ├── (auth)/
|
||
│ │ │ ├── login/
|
||
│ │ │ └── register/
|
||
│ │ ├── dashboard/
|
||
│ │ │ └── page.tsx
|
||
│ │ └── api/ # Next.js API routes
|
||
│ │ └── v1/
|
||
│ │ ├── users/
|
||
│ │ └── predictions/
|
||
│ ├── components/
|
||
│ │ ├── ui/ # shadcn/ui components
|
||
│ │ │ ├── button.tsx
|
||
│ │ │ └── card.tsx
|
||
│ │ └── features/ # Feature components
|
||
│ │ ├── predictions/
|
||
│ │ │ ├── PredictionCard.tsx
|
||
│ │ │ └── PredictionList.tsx
|
||
│ │ └── dashboard/
|
||
│ │ └── Dashboard.tsx
|
||
│ ├── lib/
|
||
│ │ ├── utils.ts # Utilities (cn helper, etc.)
|
||
│ │ ├── api.ts # API client
|
||
│ │ └── validations.ts # Zod schemas
|
||
│ ├── stores/ # Zustand stores
|
||
│ │ ├── user-store.ts
|
||
│ │ └── prediction-store.ts
|
||
│ ├── hooks/ # Custom React hooks
|
||
│ │ ├── use-user.ts
|
||
│ │ └── use-predictions.ts
|
||
│ └── types/ # TypeScript types
|
||
│ ├── user.ts
|
||
│ └── prediction.ts
|
||
├── public/ # Static assets
|
||
├── drizzle/ # Drizzle migrations
|
||
│ └── migrations/
|
||
├── tests/ # Tests (co-located or separate)
|
||
│ └── components/
|
||
└── .env.local
|
||
```
|
||
|
||
**File Structure Patterns:**
|
||
|
||
- **Tests:** Co-locés avec les fichiers (`*.test.ts`, `test_*.py`) OU dans dossier `tests/` séparé
|
||
- **Config Files:** `.env`, `.env.local`, `config.py`, `next.config.js` à la racine
|
||
- **Static Assets:** `public/` pour Next.js, `static/` pour FastAPI
|
||
- **Documentation:** `docs/` à la racine du projet
|
||
|
||
### Format Patterns
|
||
|
||
**API Response Formats:**
|
||
|
||
**Success Response:**
|
||
```json
|
||
{
|
||
"data": {
|
||
"id": 1,
|
||
"user_id": 123,
|
||
"energy_score": 0.75,
|
||
"created_at": "2026-01-15T10:30:00Z"
|
||
},
|
||
"meta": {
|
||
"timestamp": "2026-01-15T10:30:00Z",
|
||
"version": "v1"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Error Response:**
|
||
```json
|
||
{
|
||
"error": {
|
||
"code": "VALIDATION_ERROR",
|
||
"message": "Invalid user_id provided",
|
||
"details": {
|
||
"field": "user_id",
|
||
"reason": "Must be a positive integer"
|
||
}
|
||
},
|
||
"meta": {
|
||
"timestamp": "2026-01-15T10:30:00Z",
|
||
"request_id": "req_abc123"
|
||
}
|
||
}
|
||
```
|
||
|
||
**List Response:**
|
||
```json
|
||
{
|
||
"data": [
|
||
{"id": 1, "user_id": 123},
|
||
{"id": 2, "user_id": 456}
|
||
],
|
||
"meta": {
|
||
"total": 2,
|
||
"page": 1,
|
||
"per_page": 10,
|
||
"timestamp": "2026-01-15T10:30:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Data Exchange Formats:**
|
||
|
||
- **JSON Field Naming:** `snake_case` dans les réponses API (cohérence avec database)
|
||
- **Date Format:** ISO 8601 strings : `"2026-01-15T10:30:00Z"` (UTC)
|
||
- **Boolean:** `true`/`false` (pas de 1/0)
|
||
- **Null:** `null` (pas de `undefined` en JSON)
|
||
- **Arrays:** Toujours des arrays, jamais d'objets pour listes
|
||
|
||
**Type Conversions:**
|
||
- **Frontend:** Convertir `snake_case` API → `camelCase` TypeScript via transformation layer
|
||
- **Backend:** Accepter `camelCase` dans les requêtes, convertir en `snake_case` pour DB
|
||
|
||
### Communication Patterns
|
||
|
||
**Event System Patterns (RabbitMQ):**
|
||
|
||
**Event Naming:**
|
||
- Format: `{entity}.{action}` : `user.created`, `prediction.updated`, `match.analyzed`
|
||
- Lowercase avec points comme séparateurs
|
||
- Actions: `created`, `updated`, `deleted`, `analyzed`, `scraped`
|
||
|
||
**Event Payload Structure:**
|
||
```json
|
||
{
|
||
"event": "prediction.created",
|
||
"version": "1.0",
|
||
"timestamp": "2026-01-15T10:30:00Z",
|
||
"data": {
|
||
"prediction_id": 123,
|
||
"user_id": 456,
|
||
"match_id": 789
|
||
},
|
||
"metadata": {
|
||
"source": "api",
|
||
"user_id": 456
|
||
}
|
||
}
|
||
```
|
||
|
||
**State Management Patterns:**
|
||
|
||
**Zustand Store Structure:**
|
||
```typescript
|
||
interface UserState {
|
||
user: User | null;
|
||
isLoading: boolean;
|
||
error: string | null;
|
||
setUser: (user: User) => void;
|
||
clearUser: () => void;
|
||
}
|
||
|
||
const useUserStore = create<UserState>((set) => ({
|
||
user: null,
|
||
isLoading: false,
|
||
error: null,
|
||
setUser: (user) => set({ user, error: null }),
|
||
clearUser: () => set({ user: null, error: null }),
|
||
}));
|
||
```
|
||
|
||
**React Query Patterns:**
|
||
- **Query Keys:** Array format : `['users', userId]`, `['predictions', { matchId }]`
|
||
- **Mutations:** Utiliser `useMutation` avec `onSuccess`/`onError`
|
||
- **Cache Invalidation:** `queryClient.invalidateQueries(['predictions'])`
|
||
|
||
**State Update Rules:**
|
||
- **Immutable Updates:** Toujours créer de nouveaux objets/arrays
|
||
- **Action Naming:** `set{Entity}`, `update{Entity}`, `clear{Entity}`, `fetch{Entity}`
|
||
|
||
### Process Patterns
|
||
|
||
**Error Handling Patterns:**
|
||
|
||
**FastAPI:**
|
||
```python
|
||
from fastapi import HTTPException
|
||
|
||
# Standard error raising
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail={
|
||
"code": "VALIDATION_ERROR",
|
||
"message": "Invalid input",
|
||
"details": {"field": "user_id"}
|
||
}
|
||
)
|
||
```
|
||
|
||
**Next.js:**
|
||
```typescript
|
||
// API Routes
|
||
if (!user) {
|
||
return NextResponse.json(
|
||
{
|
||
error: {
|
||
code: "NOT_FOUND",
|
||
message: "User not found",
|
||
},
|
||
},
|
||
{ status: 404 }
|
||
);
|
||
}
|
||
|
||
// Components
|
||
try {
|
||
const data = await fetchUser();
|
||
} catch (error) {
|
||
// Log to Sentry
|
||
console.error("Failed to fetch user:", error);
|
||
// Show user-friendly message
|
||
setError("Unable to load user. Please try again.");
|
||
}
|
||
```
|
||
|
||
**Error Boundary:**
|
||
- Utiliser React Error Boundaries pour erreurs de rendu
|
||
- Logger toutes les erreurs à Sentry avec contexte
|
||
|
||
**Loading State Patterns:**
|
||
|
||
**Naming:**
|
||
- `isLoading` pour état de chargement principal
|
||
- `isFetching` pour refetch en arrière-plan (React Query)
|
||
- `isSubmitting` pour formulaires
|
||
|
||
**Implementation:**
|
||
```typescript
|
||
// Zustand
|
||
const useStore = create((set) => ({
|
||
isLoading: false,
|
||
setLoading: (loading) => set({ isLoading: loading }),
|
||
}));
|
||
|
||
// React Query
|
||
const { data, isLoading, isFetching } = useQuery(['users'], fetchUsers);
|
||
```
|
||
|
||
**Loading UI:**
|
||
- Skeleton loaders pour contenu principal
|
||
- Spinners pour actions rapides
|
||
- Désactiver les boutons pendant `isSubmitting`
|
||
|
||
### Enforcement Guidelines
|
||
|
||
**All AI Agents MUST:**
|
||
|
||
1. **Respecter les conventions de nommage:**
|
||
- Python: `snake_case` pour fichiers, fonctions, variables
|
||
- TypeScript: `camelCase` pour fonctions/variables, `PascalCase` pour composants/types
|
||
- Database: `snake_case` pour tables/colonnes (cohérence entre SQLAlchemy et Drizzle)
|
||
|
||
2. **Suivre la structure de projet définie:**
|
||
- Ne pas créer de fichiers en dehors des dossiers définis
|
||
- Co-locater les tests avec les fichiers OU utiliser `tests/`
|
||
- Respecter la séparation `services/`, `repositories/`, `api/`
|
||
|
||
3. **Utiliser les formats API standardisés:**
|
||
- Toujours wrapper les réponses dans `{data, meta}` ou `{error, meta}`
|
||
- Utiliser les codes d'erreur définis
|
||
- Dates en ISO 8601 UTC
|
||
|
||
4. **Implémenter la gestion d'erreurs cohérente:**
|
||
- Logger toutes les erreurs avec contexte
|
||
- Retourner des messages utilisateur-friendly
|
||
- Utiliser les patterns d'erreur définis
|
||
|
||
5. **Respecter les patterns de state management:**
|
||
- Zustand pour état UI local
|
||
- React Query pour données serveur
|
||
- Immutable updates uniquement
|
||
|
||
**Pattern Enforcement:**
|
||
|
||
- **Code Review:** Vérifier la conformité aux patterns dans chaque PR
|
||
- **Linting:** Configurer ESLint (Next.js) et flake8/black (Python) avec règles strictes
|
||
- **Type Safety:** TypeScript strict mode + Pydantic pour validation
|
||
- **Documentation:** Documenter toute exception aux patterns avec justification
|
||
|
||
**Process for Updating Patterns:**
|
||
|
||
1. Identifier le besoin de changement
|
||
2. Discuter l'impact sur l'existant
|
||
3. Mettre à jour ce document d'architecture
|
||
4. Communiquer le changement à l'équipe
|
||
5. Migrer le code existant si nécessaire
|
||
|
||
### Pattern Examples
|
||
|
||
**Good Examples:**
|
||
|
||
**Database Model (SQLAlchemy):**
|
||
```python
|
||
class User(Base):
|
||
__tablename__ = "users"
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
email = Column(String, unique=True, index=True, nullable=False)
|
||
is_premium = Column(Boolean, default=False)
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
```
|
||
|
||
**API Endpoint (FastAPI):**
|
||
```python
|
||
@router.get("/api/v1/users/{user_id}", response_model=UserResponse)
|
||
async def get_user(user_id: int):
|
||
user = await user_service.get_by_id(user_id)
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="User not found")
|
||
return {"data": user, "meta": {"timestamp": datetime.utcnow().isoformat()}}
|
||
```
|
||
|
||
**React Component:**
|
||
```typescript
|
||
export function UserCard({ userId }: { userId: number }) {
|
||
const { data: user, isLoading, error } = useQuery(
|
||
['users', userId],
|
||
() => fetchUser(userId)
|
||
);
|
||
|
||
if (isLoading) return <UserCardSkeleton />;
|
||
if (error) return <ErrorMessage error={error} />;
|
||
|
||
return <Card>{user.name}</Card>;
|
||
}
|
||
```
|
||
|
||
**Anti-Patterns:**
|
||
|
||
❌ **Mixing Naming Conventions:**
|
||
```python
|
||
# BAD
|
||
class userService: # Should be UserService
|
||
def getUserData(self): # Should be get_user_data
|
||
userId = 123 # Should be user_id
|
||
```
|
||
|
||
❌ **Inconsistent API Responses:**
|
||
```json
|
||
// BAD - Direct response
|
||
{"id": 1, "name": "John"}
|
||
|
||
// GOOD - Wrapped response
|
||
{"data": {"id": 1, "name": "John"}, "meta": {...}}
|
||
```
|
||
|
||
❌ **Mutable State Updates:**
|
||
```typescript
|
||
// BAD
|
||
state.users.push(newUser);
|
||
|
||
// GOOD
|
||
setUsers([...users, newUser]);
|
||
```
|
||
|
||
❌ **Inconsistent Error Handling:**
|
||
```python
|
||
# BAD - Different error formats
|
||
raise ValueError("Error")
|
||
return {"error": "Error"}
|
||
raise HTTPException(status_code=500)
|
||
|
||
# GOOD - Consistent format
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail={"code": "ERROR_CODE", "message": "Error"}
|
||
)
|
||
```
|