Architecture Dolibarr : ETL sans casser l’existant

Architecture : ETL « sans casser l’existant » avec Dolibarr
Comment intégrer des flux de traitement de données (ETL) dans un environnement Dolibarr déjà en production, tout en conservant la stabilité et la continuité du système ?


1. Contexte et enjeux

Point Pourquoi c’est crucial Risques associés
Stabilité du core Dolibarr Dolibarr repose sur un petit nombre de tables et de plugins très couplés. Toute modification directe sur la base ou le cœur peut provoquer des bugs ou perdre des fonctionnalités.
Évolution métier Reporting, consolidation comptable, synchronisation avec d’autres ERP, alimentation de data‑warehouse… Nécessité de mouvements de données fréquents, mais impossibles à faire « en direct » sans impacter les transactions en cours.
Non‑cassure de l’existant Garantir la continuité de service (pas de downtime, pas de perte de données ). Risque de corruption des données, perte de traçabilité, incompatibilité avec les versions futures.

Solution : préconiser un flux ETL « hors‑ligne », découpé, asynchrone et réversible, qui s’appuie sur les mécanismes natifs de Dolibarr (API, hooks, modules) plutôt que sur des modifications intrusives du noyau.


2. Principes de base d’une architecture ETL compatible Dolibarr

  1. Isolation du traitement – Le moteur ETL fonctionne en dehors du processus web de Dolibarr (cron, worker, batch).
  2. Utilisation de l’API publique – Au lieu de toucher aux tables, on utilise les fonctions PHP de Dolibarr ($dbo->insert, $object->fetch, etc.) via les hooks et le front‑controller du module.
  3. Journalisation & rollback – Chaque lot ETL est versionné, avec un journal détaillé et la capacité de restaurer l’état en cas d’erreur.
  4. Modularité – Chaque transformation (extraction, transformation, chargement) est encapsulée dans un module dédié, réutilisable et testable.
  5. Détection incrémentale – On agit uniquement sur les changements (« delta ») grâce aux champs rowid, datec, lastup_date ou à des tables de journalisation créées spécifiquement.


3. Architecture technique

3.1 Schéma global (texte)

                               +-------------------+
| Dolibarr Core |
| (BD, API, Modules)|
+----------+--------+
|
| (hooks + API)
v
+-------------------+ +-------------------+
| Scheduler / Cron |-->| Extraction (API) |
+-------------------+ +-------------------+
|
| (metadata / delta)
v
+-------------------+ +-------------------+
| Engine ETL |-->| Transformation |
| (PHP/CLI, workers) | | (Business rules) |
+-------------------+ +-------------------+
|
| (INSERT/UPDATE via API)
v
+-------------------+ +-------------------+
| Chargeur / Loader|-->| Tables cibles |
| (Bulk insert) | | (BI, DataMart) |
+-------------------+ +-------------------+

3.2 Les composants clés

Composant Rôle Implémentation Dolibarr
Scheduler Lance les lots à intervalles (ex. toutes les 5 min, quotidien). Utilise php dispatch.php cron ou un vrai cron système (*/5 * * * * /usr/bin/php /chemin/etl.php).
Extractor Récupère les nouvelles lignes ou les mises à jour via l’API ($object->fetch, $object->get.) class DolibarrMyExtractor implémente extract() et utilise les méthodes $object->fetch() avec filtre WHERE lastup_date > $lastRun.
Transformer Applique règles métier (normalisation, agrégations, mappings). Méthodes transform($row) dans un service dédié (ETL\Transformer).
Loader Charge les données dans le système cible (BD de reporting, API externe, etc.) en mode bulk. $db->insert ou API Restful ($url = 'https://.../sync'; avec requête HTTP).
Journaliseur Enregistre chaque étape, chaque ID traité, chaque erreur. Table etl_log (créée dynamiquement) ou utilisation du module Log de Dolibarr.
Rollback/Snapshot Possibilité de revenir en arrière sur les modifications temporaires. Table etl_snapshot qui stocke les rowid avant modification.


4. Gestion du delta – Ne toucher qu’à ce qui a changé

4.1 Sources de delta

Source Technique Exemple de requête
Champ datec / tdated Filtrer sur datec > $lastRun SELECT id, label FROM llx_country WHERE datec > '2025-10-28 00:00:00'
Champ lastup_date Suivi de la dernière mise à jour WHERE lastup_date > '.' . $db->quote($lastRun)
Table de journal (audit) Dolibarr ne journalise pas les changements par défaut → créer llx_etl_change via trigger ou module. INSERT INTO llx_etl_change (table_name, pk, action, ts) VALUES ('cus_customers', 123, 'UPDATE', NOW())
Compteur de version (version ou entity_version) Un champ entity_version incrémenté à chaque save. SELECT id, entity_version FROM llx_client WHERE entity_version > $lastSeenVersion

4.2 Exemple de code d’extraction (module externe)

<?php
require_once '/chemin/dolibarr/class/dolibarr.php';
$myscript = new EtlScript($db, $conf);
$lastRun = $myscript->getLastRun('product_extractor'); // stocké dans etl_log ou table dédiée
$extractor = new ProductExtractor($db);
$rows = $extractor->fetchNew($lastRun);
foreach ($rows as $row) {
$transformed = $transformer->transform($row);
$loader->load($transformed);
}
$myscript->setLastRun('product_extractor', $db->query('SELECT NOW()'));
?>

Points forts :

  • Pas de requête directe sur les tables ; on passe par $object->fetch qui déclenche les hooks.
  • Gestion centralisée du lastRun : le script peut être relancé sans craindre de duplicate.
  • Découplage : le Extraction peut être remplacé par un autre module (ex. extraction via API externe) sans toucher au Transformer ou au Loader.


5. Intégration avec les API Dolibarr (hooks & modules)

Dolibarr possède plusieurs points d’extensibilité :

Hook / Point d’entrée Utilisation ETL Exemple
foreach($hookenv as $hook => $hookFunction) Intercepter les appels get/set pour logger ou dupliquer les changements. Dans product.class.php, ajouter if ($this->isLoadedFromEtl()) { $this->saveLog(); }
AfterCreateObject / AfterUpdateObject (déclenchés dans class llx_xxx ) Après qu’un objet soit persistant, exécuter une logique ETL (ex. publier sur un bus Kafka). public function afterCreateObject() { $this->trigger('etl_after_save', array($this)); }
Hook::sendSignal Notifier d’autres processus (ex. via message queue). Hook::sendSignal('etl_process_product', array($objectId));
tecEQAutomatic (module de tâches planifiées) Planifier des appels périodiques sans code Cron external. Créez un module avec la méthode run() qui invoque votre ETL.

Bonne pratique : Ne jamais modifier les tables depuis l’extraction. Passez toujours par les méthodes de l’objet ($object->fetch(), $object->Update()). Cela assure la cohérence des business rules et les déclenchements d’événement.


6. Transformation – Règles métier sans toucher le modèle

  1. Normalisation des codes

    • Exemple : le champ type stocké en français doit être converti en code ISO : type = 'Particulier' → 'PF'.
  2. Agrégations

    • Un calcul de chiffre d’affaires mensuel à partir des lignes de facturation.
  3. Mappage de champs

    • Renommage ou fusion de colonnes (price, price_brut) → price_gross attendu par le data‑warehouse.
  4. Enrichissement

    • Appel d’une API tierce (ex. géocodage) pour ajouter latitude/longitude.

Implementation : encapsuler ces règles dans une classe Transformer avec une méthode transform(array $row): array. Ainsi, le même code peut être réutilisé par plusieurs extracteurs (produits, clients, déplacements).


7. Chargement (Load) – De l’ingestion au bulk

7.1 Bulk Insert vs Insert ligne par ligne

Méthode Avantages Inconvénients
Insert ligne par ligne via $db->insert Facile à debuguer, respecte les contraintes de Dolibarr. Lenteur sur gros volumes (> 10 k).
INSERT … VALUES (…) en bloc (ex. 100 lignes) Très rapide (prépares statements). Nécessite une validation d’unicité côté cible.
COPY / LOAD DATA (MySQL) Optimisé pour les gros volumes, minimal logging. Ne passe pas par les contraintes de déclencheurs Dolibarr.

Recommandation :

  • Phase 1 – Chargement initial / petit volume : utilisez le repository de Dolibarr ($object->add()), pour vérifier les contraintes.
  • Phase 2 – Chargement de masse : créez une table staging (ex. stg_product_tmp) puis exécutez un INSERT ... SELECT depuis cette table vers la table cible du data‑warehouse. La table staging peut être vidée entre les runs.

7.2 Exemple de script de bulk

<?php
// Table de staging temporaire
$sql = "CREATE TEMPORARY TABLE stg_product (
codproduct VARCHAR(15) NOT NULL,
label VARCHAR(255),
price DOUBLE,
last_update DATETIME
);
INSERT INTO stg_product VALUES (".$row['codproduct'].", '".$row['label']."', ".$row['price'].", '".$row['last_update']."');
...
// Chargement final (cible)
$db->query("INSERT INTO llx_product (rowid, label, price, last_update)
SELECT rowid, label, price, last_update FROM stg_product
ON DUPLICATE KEY UPDATE label=VALUES(label), price=VALUES(price), last_update=VALUES(last_update)");
?>


8. Gestion de la cohérence et du rollback

Situation Action recommandée
Erreur pendant le Transform Cancel le lot, gardez les lignes extraites dans un staging non persistant, réinitialisez le lastRun.
Échec du Load Utiliser la table etl_snapshot pour restaurer les enregistrements précédemment modifiés (ex. DELETE FROM target WHERE rowid IN (...)).
Mise à jour partielle Insérer d’abord les nouvelles lignes dans une table temp et ne les merge qu’après validation (checksum, nombre de lignes ok).
Versionning des lots Ajouter un champ batch_id (UUID) à toutes les tables de journalisation ; ce qui permet de reconstituer l’état du lot à tout moment.


9. Sécurité & Performance

Aspect Mesure
Accès aux tables Restreindre le compte d’utilisé par le worker à SELECT et INSERT uniquement sur les tables nécessaires.
Chiffrement des logs Stocker etl_log dans une table chiffrée (MySQL innodb_encrypt_tables=ON).
Contrôle des droits Créer un role dédié etl_user avec droits limités (SELECT, INSERT sur les tables de staging).
Gestion des verrous Utiliser les verrous de type SELECT … FOR UPDATE SKIP LOCKED pour éviter le double‑traitement lorsqu’un même lot peut être lancé en parallèle (ex. en mode multi‑instance).
Gestion des temps de réponse Limiter la durée maximale du workers (ex. 300 s) et prévoir un mécanisme de heartbeat pour détecter les processus bloqués.
Scalabilité Si le volume de lignes dépasse 100 k/jour, envisager un moteur de streaming (Kafka, RabbitMQ) comme bus entre l’extraction et le load.


10. Métriques de suivi (Monitoring)

Métrique Description Alert
Nombre de lignes extraites SELECT COUNT(*) FROM etl_log WHERE batch_id = X > 500 k en 30 min → warning
Temps moyen de transformation Avg(time_transform) > 5 s → investigate
Taux de succès du load SELECT COUNT(*) FROM etl_log WHERE status='OK' / total < 99 % → notification
État du batch (en cours / terminé / échoué) Champ status dans etl_batch status='FAILED' → rollback automatisé
Utilisation des ressources CPU / RAM du worker > 80 % pendant > 5 min → scaling

Ces métriques peuvent être exposées via un endpoint HTTP (/etl/status) ou directement dans le tableau de bord Grafana.


11. Exemple de cas d’utilisation complet

11.1 Besoin : Publier quotidiennement les ventes par pays vers un tableau de bord Power BI.

  1. Extraction (cron : 02:00)

    • Script sales_extractor.php récupère les lignes de llx_sales avec WHERE date >= $lastRun AND date < $today.
    • Ajoute un champ batch_id = 'batch_20251101'.
  2. Transformation (sales_transformer.php)

    • Calcule total_ht = amount * rate.
    • Normalise le code pays (FRFrance).
    • Ajoute un champ currency = 'EUR'.
  3. Load (sales_loader.php)

    • Insère les lignes dans la table stg_sales_pivot.
    • Exécute un INSERT INTO dw_sales_country SELECT … FROM stg_sales_pivot ON DUPLICATE KEY UPDATE.
  4. Journalisation

    • Chaque étape enregistre timestamp, batch_id, rows_processed, duration, status dans etl_log.
  5. Alertes

    • Si le nombre de lignes insérées diffère de > 2 % du dernier lot, envoi d’un mail à l’équipe BI.

Avantages :

  • Aucun accès direct aux tables de Dolibarr.
  • Le processus se déclenche uniquement via le scheduler intégré.
  • En cas d’erreur, le lot peut être relancé depuis le dernier batch_id valide sans perte de données.


12. Bonnes pratiques résumées

# Pratique Pourquoi
1 Ne jamais toucher les tables de Dolibarr depuis le code ETL. Garantit la compatibilité future et évite les effets de bord.
2 Utiliser les API / hooks ($object->fetch, afterUpdateObject). Respecte les règles métier et déclenche les événements.
3 Stocker le lastRun et le batch_id dans une table de métadonnées. Permet le reprise fiable d’un lot partiel.
4 Séparer les étapes (extract → transform → load) en modules réutilisables. Facilite les tests unitaires et la maintenance.
5 Facultatif : créer des tables de staging avant d’insérer dans les tables cibles. Couche de validation et de rollback.
6 Versionner les scripts (git) et les dépendances (composer). Traçabilité des changements.
7 Enrichir la monitoring (temps, lignes, erreurs). Détection précoce de dérives.
8 Planifier les flux hors‑heure de production (nuit, week‑end). Minimise l’impact sur les transactions.
9 Documenter chaque map (champ source → champ cible) dans un wiki partagé. Réduction du coût de hand‑over.
10 Réviser les processus tous les 6 mois pour intégrer les évolutions du modèle Dolibarr (nouveaux champs, nouvelles tables). Alignement continu.


13. Conclusion

Intégrer des flux ETL dans Dolibarr sans casser l’existant repose sur un principe simple : tout se fait via l’API native ou les hooks, en dehors des transactions en cours. En :

  • planifiant les extractions via des crons ou le hook tecEQAutomatic,
  • stockant les repères de traitement (lastRun, batch_id),
  • utilisant des modules dédiés pour chaque phase (extraction, transformation, load),
  • journalisant chaque étape et conservant la possibilité de rollback,

on obtient une chaîne de traitement fiable, extensible et non intrusive.

Cette architecture permet de :

  • Consolider les données vers des entrepôts de reporting ou des plateformes tierces,
  • Répondre rapidement aux exigences métier (nouvelles colonnes, nouveaux traitements) sans toucher au code source de Dolibarr,
  • Garantir la continuité de service et la conformité des données grâce à la journalisation et aux vérifications de cohérence.

En adoptant ces bonnes pratiques, les équipes IT et les responsables fonctionnels peuvent exploiter la puissance de Dolibarr tout en bénéficiant d’une infrastructure ETL robuste, prête à évoluer avec les besoins futurs.


À votre disposition pour détailler un PoC (Proof‑of‑Concept) ou créer un squelette de module ETL personnalisé selon vos processus métiers spécifiques.

Publications similaires