Pourquoi ce projet existe
Je voulais construire quelque chose qui ressemble à une vraie plateforme analytique, pas juste à une démo de dashboard posée sur quelques requêtes.
Ce qui m’intéresse dans l’analytics, ce n’est pas seulement d’écrire un modèle SQL ou de produire un graphique propre. Le vrai sujet, c’est la manière dont on découpe les responsabilités : ce que gère l’ingestion, ce que pilote l’orchestration, l’endroit où vit la logique métier, et la façon d’empêcher l’application de devenir, en douce, un deuxième warehouse.
Ce projet est donc devenu une plateforme locale de suivi de la performance de livraison à partir du dataset ecommerce Olist. Python simule les chargements historiques et incrémentaux, Airflow orchestre les exécutions, dbt porte la logique analytique, PostgreSQL expose les vues de service, et une application Next.js en lecture seule affiche les KPI finaux.
Architecture en un coup d’œil
La plateforme suit une chaîne assez claire :
- les fichiers CSV Olist embarqués servent de source transactionnelle de départ
- un simulateur Python les profile puis charge soit un bootstrap, soit des batches delta
- PostgreSQL stocke les données brutes, les métadonnées et l’état d’idempotence
- les DAG Airflow orchestrent les étapes de chargement, de transformation et de validation
- dbt construit les couches staging, intermediate, marts et serving
- le dashboard Next.js ne lit que les vues `v_fct_*` exposées dans la couche serving
J’aime bien cette architecture parce que chaque couche a un rôle net. Le simulateur ne joue pas au scheduler, Airflow ne cache pas de logique métier, et l’application n’invente pas les métriques côté navigateur.

La règle que je n’ai pas lâchée
Une règle revenait dans presque toutes les décisions : la logique des KPI ne doit exister qu’à un seul endroit.
Si la logique de retard vit d’abord dans Python, puis est réécrite dans dbt, puis recalculée dans React, le système peut encore bien se démo… mais il devient vite difficile de lui faire confiance. J’ai donc gardé la signification analytique dans dbt, et tout le reste vient simplement s’aligner autour.
“L’UI doit rester une couche de présentation posée sur un contrat warehouse fiable, pas devenir un second moteur analytique.”
Un découpage dbt qui reste lisible
C’est vraiment avec dbt que le projet prend sa forme. Le découpage en couches garde les tables proches de la source à l’entrée, la logique d’enrichissement réutilisable au milieu, et les modèles pensés pour le dashboard tout en haut.
| Couche | Schéma | Rôle |
|---|---|---|
| Raw | raw | Tables sources chargées au plus près de l’état d’origine |
| Staging | staging | Modèles proches de la source, nettoyés et renommés de manière cohérente |
| Intermediate | intermediate | Logique d’enrichissement réutilisable entre faits et dimensions |
| Marts | marts | Faits et dimensions canoniques |
| Serving | serving | Vues `v_fct_*` optimisées pour l’application |
| Metadata | meta | Contrôle des batches, statistiques de chargement et état d’idempotence |
J’ai aussi intégré dbt docs à la stack locale pour garder la lineage, les tests et les descriptions de modèles au cœur du projet, au lieu d’en faire de la documentation à part.

Airflow comme garde-fou d’orchestration
Je voulais qu’Airflow reste léger dans son rôle : planifier l’exécution, faire respecter les dépendances, rendre les échecs visibles, puis s’effacer.
- • le DAG de bootstrap valide les fichiers source, charge l’historique initial, puis déclenche la construction du warehouse
- • le DAG incrémental prévisualise le batch déterministe suivant, charge les deltas, reconstruit les couches du warehouse, lance les tests, puis ne fait avancer l’état de contrôle que si tout passe
C’est ce dernier point qui compte vraiment. Si l’ingestion échoue, les transformations ne doivent pas partir. Si les tests dbt échouent, le curseur incrémental ne doit pas avancer. Je voulais une vraie logique de contrôle, pas simplement un cron mieux emballé.

Une stack locale proprement découpée
Tout le système tourne en local dans Docker Compose. Ça paraît simple, mais cette simplicité fait partie du projet : une seule commande démarre la plateforme, tous les services partagent un réseau prévisible, et l’ensemble reste compréhensible de bout en bout.
| Service | Port | Rôle |
|---|---|---|
| postgres | 5432 | Stocke les métadonnées Airflow ainsi que les schémas analytiques |
| airflow-webserver | 8080 | UI et API Airflow |
| dbt-docs | 8081 | Expose l’interface de documentation dbt |
| nextjs-app | 3000 | Application dashboard en lecture seule |

Un dashboard léger sur un socle fiable
Le frontend est volontairement léger. Son rôle est d’exposer la performance de livraison, pas de recalculer la couche sémantique du warehouse.
La page d’accueil s’articule autour de trois surfaces : une vue d’ensemble sur le volume de commandes et les tendances de retard, un backlog de commandes en retard pour le suivi opérationnel, et une vue régionale pour comparer les performances géographiques.
C’est exactement l’équilibre que je cherchais : suffisamment de polish côté UI pour rendre le résultat utile, tout en laissant l’architecture faire l’essentiel du travail en dessous.

Lire le détail technique
J’ajouterai bientôt le lien vers la repository publique. En attendant, cet article reste le meilleur résumé de l’architecture, du découpage du warehouse, de la stack locale et du contrat de dashboard sur lequel repose le projet.