Architecture¶
Ce document décrit l'architecture complète de l'application Lead Analytics Dashboard.
Vue d'ensemble¶
graph TB
User[👤 Utilisateur] -->|HTTPS| Frontend[🌐 Frontend Next.js]
Frontend -->|REST API| Backend[⚙️ Backend FastAPI]
Backend -->|SQL| DuckDB[(🦆 DuckDB)]
Backend -->|Read/Write| DeltaLake[💾 Delta Lake]
DeltaLake -->|Storage| GCS[☁️ Google Cloud Storage]
Backend -->|Logs & Traces| Logfire[🔥 Logfire]
Notion[📝 Notion] -->|Sync| Backend
style Frontend fill:#61dafb
style Backend fill:#009688
style DuckDB fill:#ffd700
style DeltaLake fill:#ff6b6b
style GCS fill:#4285f4
style Logfire fill:#ff6600
Stack technique¶
Frontend¶
- Framework : Next.js 16 avec App Router
- UI Library : React 19
- Styling : Tailwind CSS + shadcn/ui
- Graphiques : Recharts
- HTTP Client : Axios
- Package Manager : pnpm
Caractéristiques : - Server-Side Rendering (SSR) - Client-Side Rendering (CSR) pour les composants interactifs - Optimisation automatique des images - Code splitting automatique - Hot Module Replacement (HMR)
Backend¶
- Framework : FastAPI 0.120
- Base de données analytique : DuckDB 1.4
- Stockage : Delta Lake 1.2
- Package Manager : uv
- Python : 3.13
- Observabilité : Logfire + Loguru
Caractéristiques : - API REST asynchrone - Documentation OpenAPI automatique - Validation Pydantic - CORS configuré - Logging structuré (Loguru) - Monitoring et traces en temps réel (Logfire) - Observabilité distribuée
Stockage des données¶
- Format : Delta Lake (Parquet + transaction log)
- Compression : Snappy
- Localisation : Google Cloud Storage (GCS)
gs://notion-dataascode/data_leads - Ancienne localisation :
backend/data_leads/(développement local uniquement)
Avantages : - ACID transactions - Time travel - Schema evolution - Versioning automatique - Lecture optimisée - Stockage cloud distribué et durable - Accès concurrent sécurisé
Architecture Backend¶
Structure des modules¶
backend/
├── app.py # Point d'entrée FastAPI
├── core/
│ ├── config.py # Configuration globale
│ └── openapi_docs_model.py # Modèles OpenAPI
└── routers/
├── ingestion_leads/ # Ingestion des données
│ ├── main.py # Routes d'ingestion
│ ├── model.py # Modèles Pydantic
│ ├── utils.py # Utilitaires
│ └── docs.py # Documentation OpenAPI
└── transformation/ # Transformations analytiques
├── main.py # Routes de transformation
├── docs.py # Documentation OpenAPI
└── request/ # Requêtes SQL
├── count_by_week_form_deltalake.sql
└── count_by_month_form_deltalake.sql
Flow de données¶
sequenceDiagram
participant F as Frontend
participant API as FastAPI
participant DB as DuckDB
participant DL as Delta Lake
F->>API: GET /api/v1/transformation/count_date_by_week
API->>DB: Ouvrir connexion
DB->>DL: DELTA_SCAN(data_leads)
DL-->>DB: Données brutes
DB->>DB: UNPIVOT dates
DB->>DB: DATE_TRUNC('week', date)
DB->>DB: PIVOT type_evenement
DB->>DB: GROUP BY semaine
DB-->>API: Résultats agrégés
API-->>F: JSON Response
Requêtes SQL¶
Agrégation par semaine¶
WITH unpivoted_and_weekly AS (
SELECT
DATE_TRUNC('week', date) AS semaine,
type_evenement,
date
FROM (
UNPIVOT DELTA_SCAN('gs://notion-dataascode/data_leads')
ON date_appel_booke,
date_appel_propose,
date_prise_contact,
date_relance,
date_reponse_prospect
INTO
NAME type_evenement
VALUE date
)
WHERE date IS NOT NULL
)
PIVOT unpivoted_and_weekly
ON type_evenement
USING COUNT(date)
GROUP BY semaine
ORDER BY semaine;
Étapes : 1. DELTA_SCAN : Lecture directe du format Delta Lake depuis GCS 2. UNPIVOT : Transformation des colonnes en lignes 3. DATE_TRUNC : Troncature au début de la semaine 4. PIVOT : Retransformation en colonnes par type d'événement 5. COUNT : Comptage des occurrences 6. GROUP BY : Agrégation par semaine
Architecture Frontend¶
Structure des composants¶
frontend/src/
├── app/
│ ├── layout.tsx # Layout global
│ └── page.tsx # Page d'accueil (dashboard)
├── components/
│ ├── leads-analytics-chart.tsx # Graphique principal
│ ├── monthly-objectives-table.tsx # Objectifs mensuels
│ ├── weekly-objectives-table.tsx # Objectifs hebdomadaires
│ ├── conversion-ratios-table.tsx # Ratios de conversion
│ ├── app-sidebar.tsx # Sidebar de navigation
│ └── ui/ # Composants UI réutilisables
│ ├── button.tsx
│ ├── card.tsx
│ ├── chart.tsx
│ └── ...
├── lib/
│ ├── api.ts # Client API Axios
│ └── utils.ts # Utilitaires
└── config/
└── navigation.ts # Configuration navigation
Flow de rendu¶
graph LR
A[page.tsx] --> B[LeadsAnalyticsChart]
A --> C[MonthlyObjectivesTable]
A --> D[WeeklyObjectivesTable]
A --> E[ConversionRatiosTable]
B --> F[API: /count_date_by_week]
C --> G[API: /count_date_by_month]
D --> F
E --> F
E --> G
style A fill:#61dafb
style B fill:#90ee90
style C fill:#90ee90
style D fill:#90ee90
style E fill:#90ee90
Client API¶
// lib/api.ts
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
export const api = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
});
export const getWeeklyEventCounts = async (): Promise<WeeklyEventCount[]> => {
const response = await api.get('/api/v1/transformation/count_date_by_week');
return response.data;
};
Déploiement Docker¶
Architecture multi-conteneurs¶
graph TB
subgraph "Docker Network"
Backend[🐳 Backend Container<br/>Python 3.13]
Frontend[🐳 Frontend Container<br/>Node.js 20]
end
subgraph "Google Cloud"
GCS[☁️ GCS Bucket<br/>Delta Lake]
end
Frontend -->|HTTP :8000| Backend
Backend -->|API| GCS
User[👤 Client] -->|:3000| Frontend
User -->|:8000/docs| Backend
style Backend fill:#009688
style Frontend fill:#61dafb
style GCS fill:#4285f4
Images Docker¶
Backend¶
- Base :
python:3.13-slim - Taille : ~500MB
- Build : Single-stage avec uv
- Port : 8000
Frontend¶
- Base :
node:20-alpine - Taille : ~150MB
- Build : Multi-stage (deps, builder, runner)
- Port : 3000
Réseau¶
Les conteneurs communiquent via un réseau bridge isolé.
Sécurité¶
Gestion des secrets¶
graph LR
A[.env file] --> B[docker-compose.yml]
B --> C[Environment Variables]
C --> D[Backend Runtime]
D --> E[GCS with credentials]
style A fill:#ff6b6b
style B fill:#ffd700
style C fill:#90ee90
style D fill:#009688
style E fill:#4285f4
Bonnes pratiques :
- ✅ Secrets passés via ENV au runtime
- ✅ .env dans .gitignore
- ✅ Utilisateur non-root dans les conteneurs
- ✅ Credentials GCS via Service Account JSON
- ❌ Jamais d'ARG pour les secrets
- ❌ Jamais de commit de secrets
CORS¶
Le backend autorise les requêtes du frontend :
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Performance¶
Backend¶
- Temps de réponse : 30-100ms
- Concurrence : Asynchrone avec asyncio
- Connexion DB : Pool de connexions
Frontend¶
- First Contentful Paint : < 1.5s
- Time to Interactive : < 3.5s
- Bundle size : Optimisé avec code splitting
Optimisations¶
- Compression Snappy (Delta Lake)
- Index sur les colonnes de dates
- Mise en cache HTTP (à implémenter)
- CDN pour les assets statiques (production)
Monitoring et Observabilité¶
Logfire - Observabilité en temps réel¶
L'application utilise Logfire pour une observabilité complète :
Fonctionnalités : - 📊 Traces distribuées des requêtes API - 🔍 Logs structurés avec contexte - ⚡ Métriques de performance en temps réel - 🐛 Debugging facilité avec spans détaillés - 📈 Dashboards automatiques
Configuration :
import logfire
from loguru import logger
logfire.configure(token=os.environ["LOGFIRE_TOKEN"])
logger.configure(handlers=[logfire.loguru_handler()])
Intégration : - Tous les logs Loguru sont automatiquement envoyés à Logfire - Traces FastAPI automatiques avec contexte complet - Corrélation des logs avec les traces
Logs¶
- Backend : Loguru → Logfire
- Frontend : Console logs (Next.js)
- Docker :
docker logs -f <container>
Format des logs :
2025-10-26 15:30:12 | INFO | 📊 Starting weekly aggregation...
2025-10-26 15:30:13 | INFO | ✅ Weekly aggregation completed: 24 weeks
Health Checks¶
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:8000/docs"]
interval: 30s
timeout: 10s
retries: 3
Évolutivité¶
Horizontale¶
- ☑️ Backend : Scalable avec load balancer
- ☑️ Frontend : Déploiement CDN
- ☐ Base de données : À migrer vers PostgreSQL + TimescaleDB
Verticale¶
- Augmentation des ressources CPU/RAM
- Optimisation des requêtes SQL
- Indexation supplémentaire
Prochaines étapes¶
- API Backend : Utiliser l'API
- Modules : Documentation des modules
- Installation : Installer l'application