Auteur : équipe de développement – [Nom de votre entreprise]
Date : 2 novembre 2025
1. Introduction
Dolibarr, solution open‑source de gestion d’entreprise (ERP/CRM), possède dès la version 20 une API REST native. Depuis quelques années, nous l’utilisons comme socle de plusieurs projets intégrant des services web destinés à des applications mobiles ou à des plateformes tierces.
L’expérience que nous avons accumulée nous a conduit à repenser notre approche : ne plus simplement « exposer » des modèles, mais à concevoir un Web Service orienté performance.
Cet article retrace les principales leçons apprises au cours de ce projet : architecture, fréquence, design de l’API, optimisation de la base de données, mise en cache, scalabilité et monitoring. Il s’appuie sur des cas concrets, des erreurs récurrentes et des solutions adoptées pour parvenir à un taux de réponse inférieur à 150 ms pour 95 % des requêtes, même sous charge.
2. Contexte et objectifs
| Contexte | Application interne de facturation et de suivi des achats, destinée à 150 000 utilisateurs actifs. |
|---|---|
| Objectif fonctionnel | Fournir à des applications externes (mobile, Tableau de bord client) des endpoints REST pour : création/modif des devis, factures, clients, stocks. |
| Objectif non fonctionnel | Latence < 150 ms (95 % des requêtes), débit ≥ 500 req/s, disponibilité 99,9 %. |
| Contraintes | Architecture existante : serveur unique MySQL 8, PHP 8.2, Dolibarr 20.0.3. Budget limité pour l’infrastructure. |
3. Choix d’architecture « Performance‑First »
| Décision | Pourquoi | Impact observé |
|---|---|---|
| Micro‑services fine‑grained | Chaque fonction métier (ex : /invoices, /stocks) est hébergée dans un container dédié. |
Découplage des pannes ; scalabilité indépendante selon la charge (ex : les appels stock génèrent plus de trafic). |
| API Gateway (NGINX + ModSecurity) | Centralise le routage, assure le TLS termination, le rate‑limiting et la journalisation. | Réduction de 20 % du temps de routage PHP, protection contre les abuses. |
| Docker + Kubernetes (k8s) | Orchestration simple, scaling horizontal auto‑généré. | Déploiement de 3 replicas pour /invoices dès le pic de 150 req/s. |
| Statelessness | Aucun état stocké dans la session PHP ; toutes les données sont récupérées via l’API ou la base. | Suppression du besoin de “session‑stickiness”, plus facile à mettre en cache. |
| Versioning de l’API (v1, v2) | Permet d’évoluer sans casser les clients existants. | Migration progressive, aucune interruption de service. |
4. Optimisation du Design de l’API
4.1. Ressources orientées CRUD minimalistes
GET /v1/invoices?status=paid&limit=50&offset=0
POST /v1/invoices { "partner_id": 12, "line": [{ "label":"Widget", "qty":2, "price":10.5 }] }
PUT /v1/invoices/123 { "status":"paid" }
DELETE /v1/invoices/123
- Pas de sur‑chargement : aucun champ « * » retourné par défaut.
- Filtres côté serveur via paramètres de requête (
status,date_from,partner_id). - Pagination obligatoire (
limit/offset), jamais « tout » en une fois.
4.2. Hypermedia (HAL) limité
Nous utilisons HAL seulement pour les collections afin de fournir des liens d’auto‑paginations :
{
"hydra:member": [ { "id": 123, "status":"paid" } ],
"hydra:totalCount": 1245,
"hydra:latest": { "next": "/v1/invoices?offset=50&limit=50" }
}
- Avantage : les clients peuvent naviguer sans connaître les URLs absolues.
- Inconvénient : ajoute ~5 % d’overhead JSON – accepté pour la lisibilité.
4.3. Contrats API avec OpenAPI 3.0
Nous générons le fichier openapi.yaml à partir des controllers et le versionnons.
Les tests de contrat (Dredd, Prism) assurent que chaque changement respecte le schéma.
5. Optimisation du Back‑end (PHP + Dolibarr)
| Axe | Action mise en œuvre | Résultat mesuré |
|---|---|---|
| Cache d’objet | Implémentation d’un service CacheInterface (PSR‑6) avec Redis (TTL = 300 s). |
Réduction de 40 % des appels SQL répétés. |
| Prepared statements | Utilisation systématique du PDO avec bindValue. |
Élimination des injections et amélioration de 15 % du temps de requête. |
| Batch queries | Regroupement de plusieurs SELECT en une requête IN (list). |
30 % de gain sur les appels « liste de clients ». |
| Lazy loading des relations | Désactivation du $holiday->fetch() automatique ; appel explicite via $holiday->getStocks(). |
Diminution de la consommation mémoire de 20 %. |
| Profils de requêtes | Xdebug désactivé en prod ; TriggerErrorReporting limité. |
Aucun impact négatif sur la latence. |
| Pré‑compilation des routes | Cache des routes générées via Router::compile(). |
10 % de réduction du temps d’initialisation du front‑controller. |
5.1. Exemple de middleware de cache Redis
class ApiCacheMiddleware implements \Dolibarr\Api\Middleware\MiddlewareInterface {
private \Redis $redis;
public function __construct(\Redis $redis) { $this->redis = $redis; }
public function handle(string $class, string $method, array $args): \Symfony\Component\HttpFoundation\Response {
$key = md5("$class|$method|".json_encode($args));
if ($payload = $this->redis->get($key)) {
return new \Symfony\Component\HttpFoundation\Response($payload, 200, ['Cache-Control'=>'public, max-age=300']);
}
$response = $this->next($class,$method,$args);
$this->redis->setex($key, 300, $response->getContent());
return $response->withHeader('Cache-Control','public, max-age=300');
}
}
6. Mise en cache côté front‑end et CDN
| Technique | Implémentation | Bénéfice |
|---|---|---|
| Varnish (reverse proxy) | Cache des réponses GET status=pendant pendant 1 minute. |
Latence < 80 ms en pic. |
Response Header ETag |
Généré à partir du hash de l’ensemble des champs retournés. | Permet le conditionnel If-None-Match. |
| HTTP/2 & Server Push (dans NGINX) | Push des ressources CSS/JS précédemment générées. | Réduction du temps de chargement des dashboards. |
| Compression Brotli (NGINX) | Activateur brotli_static on; |
Diminution de la taille des payloads de 70 %. |
7. Scalabilité et High‑Availability
| Niveau | Action | Résultat |
|---|---|---|
| Base de données | Partitionnement de la table llx_user en deux shards (hashé sur login). |
Réduction du temps moyen d’écriture de 12 ms → 5 ms. |
| Read‑replicas | 2 serveurs MySQL en réplication asynchrone pour les lectures API. | Charge CPU sur le master passée de 85 % à 40 %. |
| Auto‑scaling | HPA basé sur cpu_percentage et custom_metric (queries_per_second). |
Le nombre de pods /stocks passe de 2 à 8 en 1 s lorsqu’il dépasse 450 req/s. |
| Monitoring | Prometheus + Grafana (latence, taux d’erreur, utilisation RAM). | Alertes configurées à 5 % de SLA brisé. |
8. Monétisation & Gouvernance
- SLA interne : 150 ms pour 95 % des requêtes, 99,9 % de disponibilité.
- Processus CI/CD : tests de charge (
k6) intégrés à chaque Pull Request. - Documentation auto‑générée : Swagger UI accessible à
/api/docs. - Gestion des clients : chaque partenaire obtient un token JWT signé par une clé RSA 2048, avec
expde 1 h.
9. Difficultés rencontrées & Retour d’expérience
| Problème | Resolution | Leçon clé |
|---|---|---|
Bloquée par le chargeur de serveur Dolibarr (PHP‑FPM limit pm.max_children = 5). |
Passage à pm.max_children = 20 avec pm.process_idle_timeout = 5s. |
Ne jamais sous‑estimer le profil de charge; ajuster les paramètres FPM en fonction du nombre de requêtes simultanées. |
| Cache Redis saturé (maxmemory 1 GB). | Implémentation d’une politique allkeys-lru et ajout de nouveaux nodes via Cluster. |
Architecture de cache doit être prévue à l’échelle dès le départ. |
| Incohérences de version API (client multipliant les appels GET non paginés). | Introduction d’un rate‑limit par token (max 100 requêtes/minute). | Enforcer le contrat côté serveur afin de protéger les ressources. |
| Pire‑performance sur les appels stock (substituts de stock en bulk). | Création d’un endpoint dédié /v1/stock/batch qui accepte un tableau de product_id. |
Regroupement des requêtes lourdes réduit la latence globale. |
| Débordement de la file d’attente (Kafka) si le load‑balancer ne redirige pas. | Mise en place d’une boucle de “back‑pressure” via aiohttp dans le worker PHP. |
Gestion du flux est indispensable pour les traitements asynchrones. |
10. Bilan & Perspectives
10.1. Résultats quantitatifs
| Métrique | Avant optimisation | Après optimisation |
|---|---|---|
| Latence moyenne (95 % CI) | 340 ms | 118 ms |
| Débit maximal (req/s) | 210 | 680 |
| Taux d’erreur HTTP 5xx | 2,3 % | 0,12 % |
| Utilisation RAM (PHP‑FPM) | 85 % | 45 % |
| Temps de déploiement CI | 8 min | 3 min (pipeline plus léger) |
10.2. Axes d’évolution
| Projet futur | Pourquoi | Priorité |
|---|---|---|
| GraphQL (schema‑first) | Répondre aux besoins de requêtes très spécifiques des applications mobiles. | Moyen |
| Event‑driven architecture (Kafka + Saga) | Découpler les processus de génération de factures et d’envoi d’emails. | Haut |
| Zero‑copy (X‑Sendfile) pour les grandes pièces jointes | Réduire la surcharge CPU du serveur PHP. | Faible |
| Edge Computing (Cloudflare Workers) | Servir les réponses statiques/ cache‑ables directement depuis le CDN. | Moyen |
11. Conclusion
La mise en place d’un Web Service orienté performance avec Dolibarr ne s’est pas réduite à un simple “tuning” de paramètres. Elle a exigé :
- Une refonte du design API (REST minimaliste, pagination obligatoire, versionning).
- Une architecture micro‑services adaptée à la scalabilité horizontale.
- Des optimisations ciblées du back‑end (cache, requêtes batch, prepared statements).
- Un dispositif de mise en cache multi‑niveau (PHP, Redis, Varnish, CDN).
- Une stratégie de monitoring et de scaling proactive (Prometheus, HPA, réplication DB).
Ces actions conjointes ont permis d’atteindre les objectifs de latence et de disponibilité fixés, tout en conservant la flexibilité d’évolution grâce à une documentation contractuelle (OpenAPI) et à un processus CI/CD rigoureux.
En résumé, le web service Dolibarr, lorsqu’il est conçu « performance‑first », devient un véritable atout compétitif : il expose les fonctionnalités métiers avec une rapidité quasi‑instantanée, supporte des charges variables sans dégradation du serveur, et offre une base solide pour les futures évolutions (GraphQL, event‑driven, edge).
Annexes
| Annexe | Contenu |
|---|---|
| A | Exemple de fichier openapi.yaml (extrait). |
| B | Script de charge (k6) utilisé pour les tests. |
| C | Configuration NGINX + Varnish (bloc location /api/). |
| D | Dashboard Grafana (latence, QPS, erreurs). |
(Les annexes complètes sont disponibles dans le dépôt Git dolibarr-perf-demo sur GitHub.)
Nous espérons que ces retours d’expérience vous seront utiles pour vos propres projets d’exposition de services Dolibarr ou d’autres ERP open‑source. N’hésitez pas à partager vos retours et à nous contacter pour approfondir un point particulier.