# Patterns d'Architecture - ChartBastan

## Vue d'ensemble

ChartBastan utilise une architecture **client-serveur** avec:
- **Frontend:** Application React/Next.js (SPA avec SSR)
- **Backend:** API REST FastAPI
- **Communication:** REST API via HTTP
- **Architecture asynchrone:** RabbitMQ pour tâches lourdes

---

## Partie 1: Frontend Architecture (chartbastan/)

### Type d'Architecture: Component-Based avec App Router

**Framework:** Next.js 16 App Router

**Caractéristiques Principales:**

#### 1. Server Components vs Client Components

**Server Components (par défaut):**
- Exécutés sur le serveur
- Rendu HTML côté serveur
- Accès direct aux ressources serveur
- Pas d'hydratation nécessaire
- Utilisés pour: layouts, pages, data fetching initial

**Client Components (directive 'use client'):**
- Exécutés sur le client
- Hydratation côté client
- Interactivité utilisateur (events, state)
- Utilisés pour: formulaires, interactions, state local

**Exemple:**

```typescript
// Server Component (par défaut)
export default async function MatchPage() {
  const matches = await fetchMatches(); // Exécuté sur serveur
  return <MatchList matches={matches} />;
}

// Client Component
"use client";
export function MatchList({ matches }: { matches: Match[] }) {
  const [selected, setSelected] = useState(null); // State local
  return <div onClick={() => setSelected(matches[0])} />;
}
```

#### 2. App Router Structure

**Routing:** Système de fichiers dans `src/app/`

**Conventions:**
- `page.tsx` - Page principale de la route
- `layout.tsx` - Layout partagé pour la route et ses enfants
- `loading.tsx` - UI de chargement (automatic streaming)
- `error.tsx` - UI d'erreur (error boundary)
- `not-found.tsx` - UI pour 404
- `route.ts` - API routes (backend intégré)

**Exemple de structure:**

```
src/app/
├── layout.tsx          # Layout racine
├── page.tsx            # Page d'accueil (/)
├── login/
│   └── page.tsx        # Page de login (/login)
├── matches/
│   ├── page.tsx        # Liste des matchs (/matches)
│   ├── [id]/
│   │   └── page.tsx    # Détail d'un match (/matches/123)
│   └── loading.tsx     # Loading pour /matches
└── api/
    └── auth/
        └── route.ts    # API route POST /api/auth
```

#### 3. Data Fetching Strategy

**Approche Hybride:**

**Server-Side Rendering (SSR):**
- Data fetch dans Server Components
- Cache avec React Cache
- Revalidation avec revalidatePath
- Avantages: SEO, premier rendu rapide

**Client-Side Fetching:**
- React Query pour data interactive
- Cache local et synchronisation
- Refetch automatique
- Optimistic updates

**Exemple:**

```typescript
// Server Component: SSR
import { fetchMatches } from '@/lib/data';

export default async function MatchesPage() {
  const matches = await fetchMatches(); // Cache automatique
  return <MatchList initialData={matches} />;
}

// Client Component: React Query
"use client";
import { useQuery } from '@tanstack/react-query';

export function MatchList({ initialData }) {
  const { data } = useQuery({
    queryKey: ['matches'],
    queryFn: fetchMatches,
    initialData, // Hydratation depuis SSR
    refetchInterval: 30000, // Refetch toutes les 30s
  });
}
```

#### 4. State Management

**Zustand (Global State):**
- Stores légers et simples
- Pas de Provider/Context nécessaires
- Sélecteurs pour optimisation
- DevTools intégrés

**Exemple:**

```typescript
// store/user.ts
import { create } from 'zustand';

interface UserStore {
  user: User | null;
  setUser: (user: User) => void;
}

export const useUserStore = create<UserStore>((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

// Utilisation
export function UserProfile() {
  const user = useUserStore((state) => state.user);
  return <div>{user?.name}</div>;
}
```

**React Query (Server State):**
- Cache et synchronisation des données serveur
- Refetch, mutation, invalidation automatique
- Gestion optimiste
- Loading et error states

**Exemple:**

```typescript
const { data, isLoading, error } = useQuery({
  queryKey: ['matches'],
  queryFn: () => fetch('/api/matches').then(r => r.json()),
  staleTime: 5000, // Données fraîches pendant 5s
});

if (isLoading) return <Loading />;
if (error) return <Error message={error.message} />;
return <MatchList matches={data} />;
```

#### 5. Middleware

**Next.js Middleware:**
- Authentification (better-auth)
- Protection de routes
- Redirections
- Headers et cookies

**Exemple:**

```typescript
// middleware.ts
import { auth } from "@/lib/auth";

export default auth((req) => {
  const isLoggedIn = !!req.auth;
  const isOnDashboard = req.nextUrl.pathname.startsWith("/dashboard");

  if (!isLoggedIn && isOnDashboard) {
    return Response.redirect(new URL("/login", req.url));
  }
});

export const config = {
  matcher: ["/dashboard/:path*", "/api/auth/:path*"],
};
```

---

### Architecture des Composants

#### 1. Component Hierarchy

**Composition de composants:**
- Pages → Layouts → Sections → Components → UI Elements

**Exemple:**

```
MatchPage (Page)
└── MatchLayout (Layout)
    ├── MatchHeader (Section)
    │   └── MatchTitle (Component)
    ├── MatchContent (Section)
    │   ├── MatchInfo (Component)
    │   ├── MatchStats (Component)
    │   │   └── StatCard (UI Element)
    │   └── MatchPredictions (Component)
    │       └── PredictionCard (UI Element)
    └── MatchFooter (Section)
```

#### 2. Component Patterns

**Container/Presentational Pattern:**

```typescript
// Container: Logique et state
export function MatchContainer() {
  const { data, isLoading } = useMatch(matchId);
  const { predict } = usePrediction();

  if (isLoading) return <Loading />;
  return <MatchView match={data} onPredict={predict} />;
}

// Presentational: UI pure
export function MatchView({ match, onPredict }: Props) {
  return (
    <div>
      <MatchHeader match={match} />
      <PredictButton onClick={() => onPredict(match.id)} />
    </div>
  );
}
```

**Compound Components Pattern (shadcn/ui):**

```typescript
<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Title</DialogTitle>
    </DialogHeader>
  </DialogContent>
</Dialog>
```

#### 3. Custom Hooks

**Logique réutilisable:**

```typescript
// hooks/use-matches.ts
export function useMatches() {
  return useQuery({
    queryKey: ['matches'],
    queryFn: fetchMatches,
  });
}

// hooks/use-auth.ts
export function useAuth() {
  const { user } = useUserStore();
  return { isAuthenticated: !!user, user };
}
```

---

## Partie 2: Backend Architecture (backend/)

### Type d'Architecture: REST API / Layered Architecture

**Framework:** FastAPI

**Caractéristiques Principales:**

#### 1. Layered Architecture

**Couches:**

1. **API Layer** (`app/api/`)
   - Endpoints REST
   - Validation des requêtes
   - Sérialisation des réponses

2. **Service Layer** (`app/services/`)
   - Logique métier
   - Orchestration des opérations
   - Pas de dépendances directes aux modèles

3. **Repository/Model Layer** (`app/models/`)
   - Accès aux données
   - Opérations CRUD
   - Abstraction de la base de données

4. **ML Layer** (`app/ml/`)
   - Services de machine learning
   - Analyse de sentiment
   - Calcul de prédictions

5. **Scraper Layer** (`app/scrapers/`)
   - Collecte de données externes
   - Scraping Twitter/Reddit/RSS
   - Normalisation des données

**Exemple de flux:**

```
Request: GET /api/matches/1

API Layer (matches.py)
  ↓ Validate request
  ↓ Call service

Service Layer (match_service.py)
  ↓ Get match from repository
  ↓ Calculate predictions (ML service)
  ↓ Calculate energy (sentiment service)
  ↓ Aggregate data

Repository Layer (match.py)
  ↓ Query database
  ↓ Return model

API Layer
  ↓ Serialize with Pydantic
  ↓ Return response
```

#### 2. Dependency Injection

**FastAPI Depends:**
- Injection de dépendances automatique
- Test facile avec mocks
- Gestion des cycles de vie

**Exemple:**

```python
# main.py
app = FastAPI()

# Dependency: Database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# Dependency: Service
def get_match_service(db: Session = Depends(get_db)):
    return MatchService(db)

# Usage in endpoint
@app.get("/matches/{match_id}")
async def get_match(
    match_id: int,
    service: MatchService = Depends(get_match_service)
):
    return service.get_match(match_id)
```

#### 3. Pydantic Validation

**Schémas de validation:**

```python
# schemas/match.py
from pydantic import BaseModel

class MatchCreate(BaseModel):
    home_team: str
    away_team: str
    scheduled_date: datetime
    league: str

class MatchResponse(BaseModel):
    id: int
    home_team: str
    away_team: str
    scheduled_date: datetime
    predictions: List[PredictionResponse]

    class Config:
        orm_mode = True
```

**Utilisation:**

```python
@app.post("/matches", response_model=MatchResponse)
async def create_match(
    match_data: MatchCreate,
    db: Session = Depends(get_db)
):
    # Validation automatique par Pydantic
    match = Match(**match_data.dict())
    db.add(match)
    db.commit()
    return match
```

#### 4. Async/Await

**Opérations asynchrones:**

```python
# scrapers/twitter_scraper.py
async def scrape_tweets(match_id: int):
    # Opération async (non-bloquante)
    tweets = await twitter_client.search_tweets(...)
    return tweets

# main.py
@app.post("/scrape/{match_id}")
async def scrape_match(match_id: int):
    tweets = await scrape_tweets(match_id)
    return {"count": len(tweets)}
```

#### 5. Background Tasks & Workers

**Background Tasks (FastAPI):**

```python
from fastapi import BackgroundTasks

@app.post("/predict/{match_id}")
async def predict_match(
    match_id: int,
    background_tasks: BackgroundTasks
):
    # Task en arrière-plan
    background_tasks.add_task(
        calculate_prediction,
        match_id
    )
    return {"message": "Prediction started"}
```

**RabbitMQ Workers (Indépendants):**

```python
# workers/scraping_worker.py
def consume_scraping_tasks():
    connection = pika.BlockingConnection(...)
    channel = connection.channel()
    channel.queue_declare(queue='scraping')

    for method_frame, properties, body in channel.consume('scraping'):
        task = json.loads(body)
        scrape_match(task['match_id'])
        channel.basic_ack(delivery_tag=method_frame.delivery_tag)
```

---

## Architecture Intégrée (Frontend + Backend)

### Communication Pattern

**REST API Communication:**

```
Frontend Component
  ↓ React Query (Client)
  ↓ HTTP Fetch
Backend API Endpoint
  ↓ Pydantic Validation
  ↓ Service Layer
  ↓ Repository Layer
  ↓ Database (SQLAlchemy)
```

**Example Flow:**

```typescript
// Frontend: src/services/matches.ts
export function useMatches() {
  return useQuery({
    queryKey: ['matches'],
    queryFn: () => fetch('/api/matches').then(r => r.json()),
  });
}
```

```python
# Backend: app/api/matches.py
@app.get("/matches", response_model=List[MatchResponse])
async def get_matches(service: MatchService = Depends(get_match_service)):
    return service.get_all_matches()
```

```python
# Backend: app/services/match_service.py
class MatchService:
    def get_all_matches(self):
        matches = self.db.query(Match).all()
        # Add predictions, energy calculations
        return matches
```

---

## Architecture Asynchrone (RabbitMQ)

### Pattern Producer-Consumer

**Queue Architecture:**

```
Frontend Request
  ↓
Backend API (Producer)
  ↓ Publish to Queue
RabbitMQ
  ↓ Message
Worker (Consumer)
  ↓ Process Task
  ↓ Update Database
Database Update
  ↓
Frontend Polling/Websocket
```

**Example Tasks:**

1. **Scraping Task:**
   - Producer: API endpoint when user requests scrape
   - Consumer: Scraping worker
   - Task: Scrape Twitter/Reddit for match

2. **Analysis Task:**
   - Producer: Scraping worker after data collected
   - Consumer: Sentiment analysis worker
   - Task: Analyze sentiment of collected tweets/posts

3. **Prediction Task:**
   - Producer: Analysis worker after sentiment calculated
   - Consumer: Prediction worker
   - Task: Calculate prediction based on energy

---

## Architecture de Base de Données

### Phase 1: SQLite

**Schema:**
- Frontend: `chartbastan.db` (Drizzle ORM)
- Backend: Même database (SQLAlchemy)

**Tables:**
- users (authentification)
- matches (données de matchs)
- predictions (prédictions utilisateurs)
- tweets (données Twitter)
- posts (données Reddit)
- energy_scores (énergie collective)

### Phase 2: PostgreSQL

**Avantages:**
- Meilleure performance pour grandes quantités de données
- Concurrency supérieure
- Support avancé des requêtes
- Scalabilité horizontale

**Migration:**
- Même schema SQLite → PostgreSQL
- Alembic pour migrations
- Drizzle pour frontend

---

## Patterns de Sécurité

### Frontend Security

**better-auth:**
- JWT tokens
- Session management
- Password hashing (bcryptjs)
- CSRF protection

**Next.js Security:**
- Middleware pour auth
- Protected routes
- Secure cookies (httpOnly)

### Backend Security

**FastAPI Security:**
- JWT authentication
- Pydantic validation
- CORS configuration
- SQL injection prevention (SQLAlchemy)

**Example:**

```python
# middleware/auth.py
from fastapi import Security, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()

async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Security(security)
):
    token = credentials.credentials
    user = verify_token(token)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

@app.get("/protected")
async def protected_route(user = Depends(get_current_user)):
    return {"user": user}
```

---

## Patterns de Performance

### Frontend Performance

**Code Splitting:**
- Automatic code splitting (Next.js)
- Dynamic imports for heavy components
- Route-based splitting

```typescript
// Dynamic import
const MatchDetail = dynamic(() => import('@/components/MatchDetail'), {
  loading: () => <Loading />,
  ssr: false,
});
```

**Optimistic Updates:**

```typescript
const mutation = useMutation({
  mutationFn: predictMatch,
  onMutate: async (newPrediction) => {
    // Cancel pending queries
    await queryClient.cancelQueries(['predictions']);
    // Optimistic update
    queryClient.setQueryData(['predictions'], (old) => [...old, newPrediction]);
  },
  onError: (err, newPrediction, context) => {
    // Rollback on error
    queryClient.setQueryData(['predictions'], context.previousPredictions);
  },
});
```

**Streaming & Suspense:**

```typescript
export default async function Page() {
  return (
    <Suspense fallback={<Loading />}>
      <MatchList />
    </Suspense>
  );
}
```

### Backend Performance

**Async Operations:**

```python
# Multiple async operations
tweets = await scrape_twitter(match_id)
posts = await scrape_reddit(match_id)
rss = await scrape_rss(match_id)

# Process in parallel
tweets, posts, rss = await asyncio.gather(
    scrape_twitter(match_id),
    scrape_reddit(match_id),
    scrape_rss(match_id)
)
```

**Connection Pooling:**

```python
# SQLAlchemy
engine = create_engine(
    DATABASE_URL,
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True
)
```

**Caching:**

```python
from functools import lru_cache

@lru_cache(maxsize=128)
def get_prediction_model():
    # Expensive operation, cached
    return load_model()
```

---

## Résumé de l'Architecture

**Frontend (Next.js):**
- App Router (Server + Client Components)
- State: Zustand + React Query
- Data Fetching: SSR + Client-side
- UI: Tailwind + shadcn/ui

**Backend (FastAPI):**
- REST API Layered
- Service-oriented
- Async/await
- RabbitMQ workers

**Integration:**
- REST API communication
- Shared database (SQLite)
- Async processing with queues

**Architecture appropriée pour:**
- Application web moderne
- Real-time data
- Scaling horizontal
- Maintenance facile
