Initial commit

This commit is contained in:
2026-02-01 09:31:38 +01:00
commit e02db93960
4396 changed files with 1511612 additions and 0 deletions

View File

@@ -0,0 +1,969 @@
---
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"}
)
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,457 @@
---
stepsCompleted: ['step-01-init', 'step-02-discovery', 'step-03-success', 'step-04-journeys', 'step-05-domain', 'step-06-innovation', 'step-07-project-type', 'step-08-scoping', 'step-09-functional', 'step-10-nonfunctional', 'step-11-polish']
inputDocuments:
- brainstorming-session-2026-01-15.md
workflowType: 'prd'
briefCount: 0
researchCount: 0
brainstormingCount: 1
projectDocsCount: 0
classification:
projectType: web_app
domain: gambling_betting
complexity: high
projectContext: greenfield
techStack:
frontend:
framework: Next.js 16
styling: Tailwind 4
ui: shadcn/ui
backend:
primary: Python FastAPI
purpose: Core analytics, ML, sentiment analysis
secondary: Node.js (Next.js API routes)
purpose: Lightweight API, authentication
---
# Product Requirements Document - chartbastan
**Author:** Ramez
**Date:** 2026-01-15
## Success Criteria
### User Success
**Moment "Aha !" clé :** L'utilisateur voit une prédiction avec haut niveau de confiance (ex: 75%+) et cette prédiction se révèle CORRECTE.
**Métriques spécifiques :**
- Utilisateur consulte ≥ 3 prédictions consécutivement
- Utilisateur retourne sur l'app dans les 7 jours après première consultation
- Taux de conversion gratuit → premium : > 5% après 30 jours d'utilisation
- 80%+ des beta utilisateurs trouvent l'interface "facile à utiliser"
### Business Success
**Phase 1 (Backtesting & Validation) :**
- Taux de précision > 60% sur 100+ matchs historiques
- Preuve publique publiée (Medium, Reddit) avec résultats transparents
- ≥ 100 inscriptions sur liste d'attente via landing page
**Phase 2 (Lancement Beta) :**
- 500 utilisateurs beta actifs
- Engagement : > 20% DAU/MAU ratio
- ≥ 50 conversions premium (2.5% conversion rate)
**Phase 3 (Lancement Public) :**
- 2,000-5,000 utilisateurs en 2 semaines
- Viralité : chaque utilisateur invite en moyenne ≥ 2 amis
- Taux de précision en production ≥ 60% (maintenu)
**Projection Mois 6 :**
- 20,000 utilisateurs totaux
- 1,000 utilisateurs premium (19.99€/mois)
- 26,000€/mois de revenus (20,000€ premium + 6,000€ publicités)
- Seuil de rentabilité : 3 abonnés premium = rentable dès le début
### Technical Success
**Performance :**
- Dashboard mis à jour en < 3 secondes
- Analyse de 1000+ tweets en < 1 seconde
- Scraping Twitter stable sur 24h sans interruption
- Collecte réussie sans dépasser les limits de l'API publique
**Qualité des prédictions :**
- Backtesting sur ≥ 100 matchs historiques
- Taux de précision ≥ 60% (seuil de validation)
- Taux de précision < 55% = REVISER l'approche
- Confidence Meter précis : ±5% du taux de réussite réel
**Architecture :**
- API Python FastAPI stable (uptime > 99.5%)
- Frontend Next.js 16 responsive (Lighthouse score > 90)
- Base de données optimisée (SQLite suffisant pour Phase 1)
- Système de queue asynchrone (RabbitMQ) pour pics de charge
### Measurable Outcomes
**Phase 1 (Semaines 1-4) :**
- Algorithme de sentiment fonctionnel avec VADER/textblob
- Système de backtesting sur 100 matchs
- Taux de précision > 60% = VALIDÉ pour Phase 2
- Landing page avec capture emails
- Documentation publique des résultats sur Medium/Reddit
**Phase 2 (Semaines 5-8) :**
- Dashboard en temps réel (Next.js + shadcn/ui)
- 500 utilisateurs beta actifs
- Système de classement/gamification
- Notifications push pour changements majeurs
- Métriques détaillées pour utilisateurs premium
**Phase 3 (Mois 3-6) :**
- 5,000-20,000 utilisateurs
- Viralité auto-entretenue (parrainage, partage réussites)
- Taux de précision maintenu ≥ 60% en production
- Extension multi-sports (football + autres)
---
## Product Scope
### MVP - Minimum Viable Product
**Phase 1 (Semaines 1-4) - Backtesting & Validation :**
- Scrapping Twitter via API publique (1000 requêtes/heure gratuit)
- Algorithme de sentiment simple (VADER)
- Formule : Score = (Positif - Négatif) × Volume × Viralité
- Système de backtesting sur 100 matchs historiques (Ligue 1, Premier League, Champions League)
- Taux de précision > 60% = VALIDÉ
- Landing page simple avec capture d'emails (Mailchimp gratuit)
- Article/blog avec preuve de concept publique
**Contraintes MVP :**
- Budget minimal (10-20€/mois)
- Python (FastAPI) + Next.js 16 + Tailwind 4 + shadcn/ui
- Base de données SQLite (gratuit)
- Focus UNIQUE sur Football pour Phase 1
- Pas d'app mobile complexe (web app responsive suffisant)
### Growth Features (Post-MVP)
**Phase 2 (Semaines 5-8) - Lancement Beta :**
- Dashboard de prédiction en temps réel (Next.js + D3.js)
- Confidence Meter (0-100%)
- Visualisation de l'énergie collective (graphique de vague/diagramme de chaleur)
- Historique temporel (Flashback des 24h avant match)
- Système de classement/gamification (Top 100 utilisateurs)
- Programme de parrainage (invite 3 amis = 1 mois premium GRATUIT)
- Notifications push pour changements majeurs
- Comparaison Énergie vs Stats Traditionnelles
- Calendrier énergétique de matchs
- Version gratuite (1-2 prédictions/jour) + Version premium (19.99€/mois)
**Phase 3 (Mois 3-6) - Lancement Public & Croissance :**
- Système de partages automatiques de réussites
- Badges et réalisations partageables
- Marketing viral via influenceurs sportifs
- Live Tweet des prédictions pendant grands matchs
- Testimonials vidéo des gagnants
- Optimisation SEO pour traffic organique
### Vision (Future)
**Mois 6+ - Features Avancées Phase 2 :**
- Prédictions long-term (saisons complètes, classements finals)
- Détection de momentum énergétique (équipes en forme)
- Détection d'anomalies énergétiques (dark horses)
- Alertes d'énergie émergente (avantage temporel)
- Comparaison transparente vs autres méthodes (Stats, Cotes, Intuition)
- Mode Prophète pour prédictions personnalisées aux top utilisateurs
- Analyse de sentiment des joueurs eux-mêmes (Twitter accounts des joueurs)
- Historique de l'énergie pour analyse long-term
- Système de simulation de scénarios pour utilisateurs avancés
- Cross-sport predictions pour arbitrage (football, politique, marchés financiers)
**Internationalisation (Phase 2+) :**
- Multi-langue (français, anglais, espagnol, allemand, italien, portugais)
- Lancement progressif par pays (France → Espagne → Italie → Allemagne)
- Scrapping multi-plateforme par région (Twitter, Weibo, VK)
- Time-zone aware avec devise locale
**API Publique (Phase 2+) :**
- Intégration API pour développeurs tiers
- Système de souscription prémium pour données avancées
- Partners médias avec analytics prédictionnels
## User Journeys
### Persona 1 : Thomas - Le Parieur Passionné
**Situation :**
Thomas, 28 ans, supporter du PSG. Parie occasionnellement (2-3 fois par mois) sur des matchs importants. A perdu 400€ le mois dernier en misant sur des équipes favorites sans vraie stratégie.
**But :**
Il veut augmenter ses chances de gagner en utilisant des données objectives plutôt que son instinct ou les cotes traditionnelles qu'il ne comprend pas vraiment.
**Obstacle :**
Les sites de paris sont confus, les cotes sont opaques, et il n'a pas les compétences techniques pour analyser les stats des joueurs ou l'historique des matchs.
**Parcours Narratif - Histoire de Thomas :**
**Scène d'ouverture :**
Un jeudi soir, Thomas scrolle Twitter sur son canapé. Il voit un tweet viral : "Comment l'énergie collective des supporters sur Twitter prédit 65% des résultats de football." Intrigué, il clique sur le lien.
**Action Montante (Étape 1 - Découverte) :**
Il atterrit sur une landing page épurée avec des résultats de backtesting. Il voit un tableau de 100 matchs récents avec prédictions et résultats réels. Le taux de réussite est de 63%. Thomas se dit : "C'est mieux que Polymarket qui a seulement 55% de précision." Il inscrit son email dans la liste d'attente.
**Action Montante (Étape 2 - Premier Accès) :**
Deux jours plus tard, il reçoit un email : "Ton accès est prêt ! Tu as 1 prédiction gratuite par jour." Il se connecte. L'interface est ultra-simple : un match PSG vs Marseille ce soir avec un "Confidence Meter" à 78% pour PSG. En dessous, une vague visuelle montre l'énergie collective monter doucement depuis 2 heures.
**Climax - Le Moment "Aha !" :**
Thomas regarde le match sur TV. À la 75ème minute, PSG marque le deuxième but. Son téléphone vibre : notification de l'app : "Score de confiance maintenant 82% - Énergie collective stabilisée." Thomas réalise que l'app n'avait pas seulement prédit le résultat, mais montrait l'évolution de l'énergie en temps réel. Il gagne son pari de 50€.
**Résolution - Nouvelle Réalité :**
Thomas revient sur l'app le lendemain. Il regarde son historique personnel : 3 prédictions consultées, 2 correctes (67%). Il comprend que ce n'est pas parfait, mais c'est mieux que son instinct. Il reçoit une notification : "Invite 3 amis et obtiens 1 mois premium GRATUIT." Il partage sur son groupe WhatsApp de supporters. Trois amis s'inscrivent. Thomas obtient son mois premium et découvre les fonctionnalités avancées.
---
### Persona 2 : Marie - La Parieuse Sérieuse
**Situation :**
Marie, 34 ans, analyste financière. Parie régulièrement sur le football depuis 5 ans. A perdu 2,000€ l'année dernière mais y voit un investissement, pas du gambling.
**But :**
Elle veut des métriques détaillées, des données historiques, et des analyses avancées pour prendre des décisions informées. Elle est prête à payer pour de la valeur réelle.
**Obstacle :**
Les sites de paris ne lui donnent que des cotes superficielles. Elle veut comprendre POURQUOI une prédiction est faite, pas juste QUOI parier.
**Parcours Narratif - Histoire de Marie :**
**Scène d'ouverture :**
Marie lit un article sur Medium : "Pourquoi l'énergie collective des supporters prédit mieux que l'analyse statistique traditionnelle." Elle est sceptique mais curieuse - elle connait bien les analyses financières. Elle s'inscrit sur l'app pour tester.
**Action Montante (Étape 1 - Version Gratuite) :**
Pendant 2 semaines, elle utilise la version gratuite (1 prédiction par jour). Elle teste 14 prédictions sur 14 jours. Taux de réussite : 64%. Elle est impressionnée mais frustrée - l'accès limité l'empêche de vraiment exploiter l'outil.
**Action Montante (Étape 2 - Conversion Premium) :**
Le 15ème jour, elle reçoit un email : "Marie, vous avez consulté 15 prédictions avec 64% de réussite. Passez Premium pour des analyses illimitées et métriques détaillées." Elle regarde les avantages : accès temps réel, alertes push, dashboard avancé, métriques détaillées. Elle se dit : "19.99€/mois, c'est moins que ce que je perds en un mauvais pari." Elle s'abonne.
**Climax - La Révélation Premium :**
Marie accède au dashboard avancé. Elle découvre une vue qu'elle n'avait jamais vue :
- Graphique d'énergie collective sur 24h pour chaque équipe
- Comparaison côte à côte : Prédiction Énergie vs Stats Traditionnelles vs Cotes
- Alertes programmées : "Énergie de l'équipe X s'effondre -3% en 30 min"
- Son historique personnel avec ROI détaillé : +240€ depuis inscription
Pour le match Chelsea vs Arsenal, elle voit une anomalie : Chelsea a 85% d'énergie collective mais les cotes favorisent Arsenal. Elle regarde l'actualité intégrée : un star player d'Arsenal est blessé, mais les médias ne l'ont pas encore annoncé. Marie mise sur Chelsea avec confiance. Chelsea gagne 3-0.
**Résolution - Nouvelle Réalité :**
Après 2 mois de Premium, Marie a un ROI de +18%. Elle recommande l'app à ses collègues traders. Elle devient une "Top Prophète" dans le classement public, ce qui augmente sa crédibilité dans sa communauté de parieurs. Elle reçoit des demandes d'autres utilisateurs qui veulent ses conseils.
---
### Persona 3 : Julien - Le Testeur Beta Technique
**Situation :**
Julien, 24 ans, développeur Python full-stack. Passionné de football et de data science. A entendu parler du projet sur Reddit.
**But :**
Il veut participer à la beta parce qu'il est fasciné par l'innovation technique. Il espère aussi contribuer avec des feedbacks constructifs.
**Obstacle :**
Il ne sait pas si l'app est stable, si les prédictions sont réellement précises, ou si c'est juste une autre fausse promesse dans le monde du betting.
**Parcours Narratif - Histoire de Julien :**
**Scène d'ouverture :**
Julien lit un post sur r/DataScience : "Nouveau projet : Analyser l'énergie collective Twitter pour prédire les résultats de football avec 63% de précision." Il commente : "C'est bluffant mais j'ai besoin de voir le code." Le fondateur répond : "Rejoins la beta, tu auras accès à l'API documentation."
**Action Montante (Étape1 - Onboarding Beta) :**
Julien s'inscrit à la beta limitée. Il reçoit immédiatement :
- Accès au dashboard de prédiction
- Documentation API publique
- Formulaire de feedback structuré
- Invitation sur Discord pour testers beta
Il teste la première prédiction pour Real Madrid vs Barça. Dashboard met 2.5 secondes à charger (acceptable mais perfectible). Confidence Meter à 71%. Julien note le feedback.
**Action Montante (Étape 2 - Exploration Technique) :**
Il connecte son propre script Python à l'API publique. Il teste 50 requêtes par minute pendant 1 heure. Rate limiting fonctionne correctement. Il analyse les données JSON reçues :
```json
{
"match": "Real Madrid vs Barça",
"energy_score": 0.71,
"confidence": 71,
"sentiment_breakdown": {
"positive": 654,
"negative": 234,
"neutral": 123
},
"viral_coefficient": 3.2,
"time_weight": 0.85
}
```
Julien est impressionné par la structure de données claire et la transparence de l'algorithme.
**Climax - Le Bug Critique :**
Pendant un match PSG vs Lyon, Julien remarque une anomalie : l'énergie collective chute brutalement de 82% à 45% en 2 minutes, mais le match continue normalement. Il suspecte un bug dans l'algorithme de pondération temporelle. Il ouvre le formulaire de feedback beta, capture les screenshots, et décrit le problème avec détails techniques : timestamps, JSON brut, comportement attendu vs observé.
**Résolution - Nouvelle Réalité :**
24h plus tard, il reçoit un email : "Merci Julien ! Tu as trouvé un bug dans notre algorithme de filtrage du chaos. On l'a corrigé et ta version de beta a été mise à jour. Comme remerciement, tu as 3 mois premium gratuits." Julien teste à nouveau le match PSG vs Lyon - le bug est résolu. Il poste sur Reddit : "L'équipe de dev est réactive et transparente. Le projet a du potentiel." Son post génère 47 inscriptions sur la liste d'attente.
---
### Persona 4 : Sophie - Admin & Opérations
**Situation :**
Sophie, 29 ans, ingénieure DevOps. Responsable de la stabilité et de la performance du système.
**But :**
Maintenir l'uptime > 99.5%, surveiller les API externes (Twitter, Reddit, RSS), et résoudre les incidents avant qu'ils n'affectent les utilisateurs.
**Obstacle :**
Le système dépend de trois API externes (Twitter, Reddit, RSS) avec des rate limits différents et des patterns de panne imprévisibles.
**Parcours Narratif - Histoire de Sophie :**
**Scène d'ouverture :**
Sophie arrive au bureau un lundi matin à 8h30. Son premier réflexe : ouvrir le dashboard de monitoring. Tout est vert - uptime 99.7% sur les 7 derniers jours. Le système de queue asynchrone (RabbitMQ) gère correctement les pics de charge du week-end.
**Action Montante (Étape 1 - Monitoring Quotidien) :**
Elle vérifie les métriques clés :
- **Twitter API** : 850 req/heure sur 1000 disponibles (85% utilisation)
- **Reddit API** : Pas de rate limiting (API généreuse)
- **RSS Feeds** : 100% succès
- **Temps de réponse** : 2.3s moyen (< 3s requis)
- **Taux de précision** : 64% sur les 50 derniers matchs
Tout semble nominal. Mais elle remarque une tendance inquiétante : l'utilisation de l'API Twitter augmente progressivement depuis 3 jours (70% → 75% → 82% → 85%).
**Action Montante (Étape 2 - Analyse de Tendance) :**
Sophie analyse les logs détaillés. Elle découvre que le volume de tweets par match a augmenté de 40% depuis une semaine - c'est saison de Champions League. Le système de scraping va bientôt dépasser le rate limit de Twitter.
Elle identifie trois options :
1. Optimiser les requêtes Twitter (réduire la quantité)
2. Prioriser les matchs VIP (tous les matchs, moins de profondeur par match)
3. Passer à API Twitter payante (mais budget Phase 1 = 0)
Elle décide de contacter l'équipe produit pour validation.
**Climax - L'Incident Critique :**
Mercredi soir, pendant un match PSG vs Bayern (grand événement), Sophie reçoit une alerte automatique sur son téléphone : "⚠️ CRITICAL - Twitter API rate limit dépassé - Scraping STOPPÉ."
Elle se connecte immédiatement. Le scraping Twitter est arrêté depuis 18:42. Les prédictions continuent mais avec uniquement Reddit + RSS, ce qui baisse la confiance de 67% à 58%.
Sophie exécute le plan d'urgence qu'elle a préparé :
1. Active le mode "dégradé" (Reddit + RSS uniquement)
2. Ajoute une bannière dans l'app : "Données Twitter tempor. indisponibles - Prédictions avec confiance réduite"
3. Envoie notification aux utilisateurs premium concernés
4. Analyse les logs pour comprendre si c'est une anomalie ou une nouvelle normalité
**Résolution - Nouvelle Réalité :**
Le lendemain, Sophie analyse l'incident :
- Cause : Volume record de tweets pendant PSG vs Bayern (127,000 tweets/h vs moyenne 85,000/h)
- Impact : Prédictions avec 58% de confiance vs 67% habituel
- Durée : 47 minutes
Elle propose une solution permanente :
- **Optimisation intelligente** : Scraper uniquement les tweets avec engagement élevé (>10 retweets/likes)
- **Mode priorité dynamique** : Les matchs VIP (PSG, Bayern, Real Madrid, Barça) ont la priorité sur les autres
- **Alerte prédictive** : Alerter quand l'utilisation > 90% pendant 10 minutes consécutives
L'équipe produit valide. Sophie implémente en 2 jours. La prochaine période de pic est gérée sans incident.
---
### Persona 5 : Lucas - Support Client
**Situation :**
Lucas, 26 ans, support client. Répond aux tickets d'aide, guide les utilisateurs, et résout les problèmes courants.
**But :**
Aider les utilisateurs rapidement, réduire le taux de churn, et collecter des feedbacks constructifs pour l'équipe produit.
**Obstacle :**
L'app a deux types d'utilisateurs très différents (gratuits vs premium) avec des attentes différentes. Les questions techniques peuvent être complexes.
**Parcours Narratif - Histoire de Lucas :**
**Scène d'ouverture :**
Lucas ouvre son dashboard de support à 9h du matin. 12 nouveaux tickets dans la file d'attente depuis la veille. Il classe par priorité : 2 critiques, 4 importants, 6 normaux.
**Action Montante (Étape 1 - Support Premium) :**
Le premier ticket est critique : "User premium depuis 3 mois - Dashboard affiche 'Erreur 500' depuis 2h - En pleine période de pari sur Champions League !"
Lucas identifie immédiatement : c'est un utilisateur premium (ticket marqué avec badge ⭐). Il vérifie d'abord s'il y a des incidents système connus. Monitoring montre "All Systems Green". C'est donc un problème spécifique à cet utilisateur.
Il répond dans les 5 minutes (SLA premium) :
> "Bonjour Marc ! Je suis Lucas du support Premium. Je vois que votre dashboard rencontre une erreur 500. Pourriez-vous me dire : 1) Sur quel navigateur êtes-vous ? 2) À quel moment exactement l'erreur apparaît ? Je vais prioriser votre résolution."
Marc répond : "Chrome, j'essaie de charger l'historique de mes prédictions des 30 derniers jours."
Lucas teste sur son propre compte avec le même scénario. Il reproduit l'erreur ! C'est un bug récent, pas un problème spécifique à Marc.
**Action Montante (Étape 2 - Escalade Technique) :**
Il identifie le pattern : seuls les utilisateurs premium depuis plus de 3 mois sont affectés quand ils chargent l'historique complet. C'est probablement une base de données qui ne gère pas correctement les requêtes massives.
Il escalade à l'équipe technique avec priorité P1 (bug critique) :
```
Bug: Dashboard Premium - Erreur 500 sur historique 30+ jours
Impact: Utilisateurs premium > 3 mois
Reproduction: Charger historique complet
Urgency: P1 - Période de pari active
```
Pendant que l'équipe corrige, Lucas prépare une réponse proactive : il contacte tous les utilisateurs premium concernés (identifiés dans la base de données) avec :
> "Nous avons identifié un bug sur l'historique. Notre équipe corrige activement. En attendant, utilisez l'historique des 7 derniers jours (fonctionnel). Désolé pour l'inconvénient !"
**Climax - Le Feedback Constructif :**
Une fois le bug résolu, Lucas reçoit un email de Marc :
> "Merci pour la réponse rapide et pour avoir prévenu les autres. J'ai perdu 1 pari sur 3 ce week-end, mais votre transparence m'a donné confiance dans l'équipe. Une suggestion : vous pourriez montrer un indicateur de performance sur l'historique (temps de chargement prévu)."
Lucas documente ce feedback dans le système de tickets. Il voit que cette suggestion est venue 3 fois dans le dernier mois. Il la tagge pour l'équipe produit : "Feature Request - Performance Indicator sur Historique".
**Résolution - Nouvelle Réalité :**
Après 6 mois, Lucas observe une amélioration : le taux de tickets a diminué de 35% car l'équipe produit a implémenté les features demandées par les utilisateurs. Les utilisateurs premium restent en moyenne 18 mois (vs 12 mois avant les améliorations).
---
### Journey Requirements Summary
**Capacités Révélées par les Parcours :**
**Pour Utilisateur Primaire (Thomas/Marie) :**
- Landing page avec backtesting visible
- Dashboard simple et intuitif (Mode Débutant)
- Confidence Meter visuel (0-100%)
- Visualisation de l'énergie collective (vague/graphique)
- Historique personnel avec ROI
- Système de classement/gamification
- Programme de parrainage
- Notifications push
- Mode Expert avec métriques détaillées
**Pour Utilisateur Beta (Julien) :**
- Documentation API publique
- Formulaires de feedback structurés
- Channels de communication (Discord, formulaire)
- Système de récompenses pour feedbacks
- Rate limiting robuste
- Communication transparente
**Pour Admin/Opérations (Sophie) :**
- Dashboard de monitoring temps réel
- Alertes automatiques multi-canal
- Logs détaillés et analyse d'incidents
- Configuration dynamique de priorités
- Plans d'urgence et modes dégradés
- Communication transparente avec utilisateurs
**Pour Support (Lucas) :**
- Système de tickets avec SLA différenciés
- Dashboard de support avec classification
- Système d'escalade technique
- Communication proactive
- Système de feedbacks structuré
- Base de connaissance
**Pour Sources de Données (Hybride) :**
- Scrapping Twitter API (60% pondération)
- Scrapping Reddit API (25% pondération)
- Scrapping RSS Feeds (15% pondération)
- Fusion intelligente avec pondération
- Rate limiting robuste et priorisation dynamique

View File

@@ -0,0 +1,367 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chartbastan - Design Directions</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--color-primary: #0A84FF;
--color-primary-hover: #0870E6;
--color-background: #FFFFFF;
--color-surface: #F5F5F7;
--color-text-primary: #1C1C1E;
--color-text-secondary: #6E6E73;
--color-border: #D1D1D6;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--radius-lg: 18px;
--radius-pill: 980px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--color-background);
color: var(--color-text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: var(--spacing-xl);
}
header {
text-align: center;
margin-bottom: 64px;
padding: 64px 32px;
}
header h1 {
font-size: 56px;
font-weight: 700;
letter-spacing: -0.015em;
margin-bottom: 16px;
}
header p {
font-size: 21px;
font-weight: 400;
color: var(--color-text-secondary);
max-width: 680px;
margin: 0 auto;
}
.direction-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 32px;
margin-bottom: 64px;
}
.direction-card {
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 32px;
transition: all 200ms ease;
}
.direction-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.direction-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border);
}
.direction-title {
font-size: 24px;
font-weight: 700;
letter-spacing: -0.015em;
}
.direction-badge {
padding: 6px 16px;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
background: var(--color-surface);
color: var(--color-text-primary);
}
.direction-description {
font-size: 17px;
color: var(--color-text-secondary);
line-height: 1.6;
margin-bottom: 24px;
}
.direction-features {
list-style: none;
margin-bottom: 24px;
}
.direction-features li {
padding: 8px 0;
padding-left: 24px;
position: relative;
font-size: 15px;
color: var(--color-text-secondary);
}
.direction-features li::before {
content: "•";
position: absolute;
left: 0;
color: var(--color-primary);
font-weight: bold;
}
.mockup-preview {
background: var(--color-surface);
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
min-height: 300px;
display: flex;
flex-direction: column;
gap: 16px;
}
.mockup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: var(--color-background);
border-radius: 8px;
}
.mockup-title {
font-size: 20px;
font-weight: 600;
}
.mockup-confidence {
font-size: 28px;
font-weight: 700;
color: var(--color-primary);
}
.mockup-content {
background: var(--color-background);
border-radius: 8px;
padding: 16px;
}
.mockup-match {
padding: 16px;
border-bottom: 1px solid var(--color-border);
}
.mockup-match:last-child {
border-bottom: none;
}
.match-teams {
font-size: 17px;
font-weight: 600;
margin-bottom: 8px;
}
.match-confidence {
font-size: 15px;
color: var(--color-text-secondary);
}
button {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 17px;
font-weight: 500;
padding: 12px 28px;
border-radius: var(--radius-pill);
border: none;
cursor: pointer;
background: var(--color-primary);
color: var(--color-background);
transition: all 200ms ease;
width: 100%;
}
button:hover {
background: var(--color-primary-hover);
transform: scale(1.02);
}
@media (max-width: 768px) {
.direction-grid {
grid-template-columns: 1fr;
}
header h1 {
font-size: 40px;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Chartbastan - Design Directions</h1>
<p>Explorez différentes directions de design pour trouver celle qui correspond le mieux à votre vision.</p>
</header>
<div class="direction-grid">
<!-- Direction 1: Minimaliste Centré -->
<div class="direction-card">
<div class="direction-header">
<div class="direction-title">Direction 1: Minimaliste Centré</div>
<div class="direction-badge">Sobre</div>
</div>
<div class="direction-description">
Design épuré avec focus sur l'essentiel. Information hiérarchisée, espace blanc généreux, navigation simple.
</div>
<ul class="direction-features">
<li>Layout centré avec largeur max 800px</li>
<li>Navigation bottom bar simple</li>
<li>Cards espacées verticalement</li>
<li>Confidence Meter en vedette</li>
</ul>
<div class="mockup-preview">
<div class="mockup-header">
<div class="mockup-title">Matchs du Jour</div>
<div class="mockup-confidence">78%</div>
</div>
<div class="mockup-content">
<div class="mockup-match">
<div class="match-teams">PSG vs Marseille</div>
<div class="match-confidence">Confiance : 78%</div>
</div>
<div class="mockup-match">
<div class="match-teams">Lyon vs Saint-Étienne</div>
<div class="match-confidence">Confiance : 62%</div>
</div>
</div>
</div>
<button>Voir cette direction</button>
</div>
<!-- Direction 2: Dashboard Dense -->
<div class="direction-card">
<div class="direction-header">
<div class="direction-title">Direction 2: Dashboard Dense</div>
<div class="direction-badge">Informatif</div>
</div>
<div class="direction-description">
Layout dense avec beaucoup d'informations visibles. Grid system, multiples cards, métriques détaillées.
</div>
<ul class="direction-features">
<li>Grid 2-3 colonnes sur desktop</li>
<li>Cards compactes avec plus d'infos</li>
<li>Navigation tabs horizontale</li>
<li>Statistiques visibles en permanence</li>
</ul>
<div class="mockup-preview">
<div class="mockup-header">
<div class="mockup-title">Dashboard</div>
<div class="mockup-confidence">67%</div>
</div>
<div class="mockup-content">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div class="mockup-match">
<div class="match-teams">PSG</div>
<div class="match-confidence">78%</div>
</div>
<div class="mockup-match">
<div class="match-teams">Lyon</div>
<div class="match-confidence">62%</div>
</div>
</div>
</div>
</div>
<button>Voir cette direction</button>
</div>
<!-- Direction 3: Card-First -->
<div class="direction-card">
<div class="direction-header">
<div class="direction-title">Direction 3: Card-First</div>
<div class="direction-badge">Moderne</div>
</div>
<div class="direction-description">
Chaque match est une card grande et immersive. Swipe navigation, focus sur un match à la fois.
</div>
<ul class="direction-features">
<li>Cards full-width avec images</li>
<li>Swipe gauche/droite entre matchs</li>
<li>Confidence Meter intégré dans card</li>
<li>Navigation minimaliste</li>
</ul>
<div class="mockup-preview">
<div class="mockup-content" style="padding: 0;">
<div style="background: var(--color-surface); padding: 32px; border-radius: 12px;">
<div class="match-teams" style="font-size: 24px; margin-bottom: 16px;">PSG vs Marseille</div>
<div class="mockup-confidence" style="font-size: 48px; margin-bottom: 16px;">78%</div>
<div class="match-confidence">Confiance haute</div>
</div>
</div>
</div>
<button>Voir cette direction</button>
</div>
<!-- Direction 4: List-First -->
<div class="direction-card">
<div class="direction-header">
<div class="direction-title">Direction 4: List-First</div>
<div class="direction-badge">Rapide</div>
</div>
<div class="direction-description">
Liste verticale simple et rapide. Scan visuel facile, informations essentielles, actions rapides.
</div>
<ul class="direction-features">
<li>Liste verticale scrollable</li>
<li>Items compacts avec confidence visible</li>
<li>Pull-to-refresh natif</li>
<li>Actions rapides (tap pour détails)</li>
</ul>
<div class="mockup-preview">
<div class="mockup-content" style="padding: 0;">
<div class="mockup-match" style="display: flex; justify-content: space-between; align-items: center;">
<div>
<div class="match-teams">PSG vs Marseille</div>
<div class="match-confidence">Aujourd'hui 20h00</div>
</div>
<div class="mockup-confidence" style="font-size: 24px;">78%</div>
</div>
<div class="mockup-match" style="display: flex; justify-content: space-between; align-items: center;">
<div>
<div class="match-teams">Lyon vs Saint-Étienne</div>
<div class="match-confidence">Aujourd'hui 22h00</div>
</div>
<div class="mockup-confidence" style="font-size: 24px; color: var(--color-text-secondary);">62%</div>
</div>
</div>
</div>
<button>Voir cette direction</button>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff