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 :
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.
Le dashboard ne lit que les vues `serving.v_fct_*`. Il n’accède jamais directement aux tables raw, staging, intermediate ou mart.
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.
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
Le code complet est documenté dans le repo : simulation de batches Python, DAG Airflow, couches dbt, vues PostgreSQL de service, stack Docker Compose et dashboard Next.js en lecture seule.
https://github.com/scottsantinho/retail-sales-project-public