# ADR-005 : Base de Données

**Statut** : Accepté
**Date** : 2025-01-17

## Contexte

Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute.

## Décision

- **PostgreSQL + PostGIS** : Données persistantes et requêtes géospatiales
- **PgBouncer** : Connection pooling pour PostgreSQL
- **Redis Cluster** : Cache géolocalisation et sessions

## Architecture

```mermaid
flowchart LR
    A[Requête API]
    B[Redis Cache]
    C[PgBouncer]
    D[PostgreSQL + PostGIS]
    E[Réponse]

    A --> B
    B -->|HIT| E
    B -->|MISS| C
    C --> D
    D --> C
    C --> B
    B --> E
```

## Alternatives considérées

| Usage | Option choisie | Alternatives |
|-------|---------------|--------------|
| Données utilisateurs | PostgreSQL | MySQL, MongoDB |
| Géolocalisation | PostGIS | MongoDB Geo, Elasticsearch |
| Cache | Redis | Memcached, KeyDB |
| Analytics (futur) | ClickHouse | TimescaleDB |

## Justification

### PostgreSQL + PostGIS
- Requêtes géospatiales complexes et précises
- Index GIST pour performance
- ACID, fiabilité éprouvée
- Écosystème mature

### PgBouncer
- **Connection pooling** : Réduit l'overhead de création de connexions PostgreSQL
- **Mode transaction** : Connexion réutilisée entre transactions (optimal pour API stateless)
- **Performance** : Permet de gérer 1000+ connexions concurrentes avec ~100 connexions réelles à PostgreSQL
- **Scaling** : Essentiel pour supporter la montée en charge sans surcharger PostgreSQL
- **Port** : :6432 (vs :5432 pour PostgreSQL direct)

### Redis
- Cache géo natif (`GEORADIUS`) : 100K+ requêtes/sec
- Sessions utilisateurs
- Pub/sub pour temps réel

## Exemple de requête

```sql
SELECT id, name,
       ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
FROM contents
WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
ORDER BY distance
LIMIT 20;
```

## Conséquences

- TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas)
- Index GIST sur colonnes géométriques
- Réplication read replicas pour scaling lecture

### Configuration PgBouncer

**Mode recommandé** : `transaction`
- Connexion libérée après chaque transaction
- Optimal pour API stateless (Go + Fiber)
- Maximise la réutilisation des connexions

**Pool sizing** :
- `default_pool_size` : 20 (connexions par base)
- `max_client_conn` : 1000 (connexions clients max)
- `reserve_pool_size` : 5 (connexions de secours)

**Configuration type** (`pgbouncer.ini`) :
```ini
[databases]
roadwave = host=localhost port=5432 dbname=roadwave
zitadel = host=localhost port=5432 dbname=zitadel

[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = scram-sha-256
pool_mode = transaction
default_pool_size = 20
max_client_conn = 1000
reserve_pool_size = 5
server_idle_timeout = 600
```

**Connexion application Go** :
```go
// Avant (PostgreSQL direct)
// dsn := "postgres://user:pass@localhost:5432/roadwave"

// Après (via PgBouncer)
dsn := "postgres://user:pass@localhost:6432/roadwave"
```

## Documentation technique détaillée

- [Diagramme de séquence cache géospatial](../architecture/sequences/cache-geospatial.md)
- [Schéma base de données](../architecture/database/schema.md)
