Chargement…
Chargement…
Case study orienté impact: objectif métier clair, exécution maîtrisée, preuve ROI et plan de delivery traçable.
Impact principal
−28h/semaine ops
Valeur mesurable sur le flux critique
ROI business
Équivalent 0.7 ETP libéré
Référence client observée en production
Time-to-delivery
6 semaines
Cycle complet cadrage → go-live
Scope livré
6 livrables
Briques prêtes à exploiter par les équipes
Avant
28h/semaine d'opérations manuelles — chaque nouvelle automatisation = prestataire externe
Après
12 workflows tournent seuls 24/7 — équipe opé autonome sur les modifications
Aperçu produit
Workflows
Lead pipeline quotidien
Rapport hebdo Slack
Alerte rupture stock
Email relance J+3
Cron 08:00
triggerFetch Leads
actionScore IA > 70?
conditionSlack notif
actionArchive lead
actionPush CRM
actionLogs d'exécution · dernier run
Architecture système
Triggers
Cron · Webhook · Email
Éditeur React Flow
Drag & drop nodes
Orchestration
n8n API · parallèle
Logs temps réel
WebSocket · historique
Alertes erreurs
Slack · Email · PagerDuty
Code — extrait anonymisé
1// Parallel workflow execution with resource locking2async function executeWorkflow(wf: Workflow): Promise<RunResult> {3 const dag = buildDAG(wf.nodes, wf.edges)4 const visited = new Set<string>()5 const results = new Map<string, NodeOutput>()67 async function runNode(nodeId: string): Promise<void> {8 if (visited.has(nodeId)) return9 visited.add(nodeId)1011 // Run all upstream dependencies first (parallel)12 const upstream = dag.getParents(nodeId)13 await Promise.all(upstream.map(runNode))1415 const node = wf.nodes.find((n) => n.id === nodeId)!16 const inputs = collectInputs(nodeId, results)1718 try {19 const output = await executeNodeType(node, inputs)20 results.set(nodeId, output)21 await db.logs.create({ workflowId: wf.id, nodeId, output })22 } catch (err) {23 await notifyError(wf, nodeId, err)24 throw err25 }26 }2728 const sinks = dag.getSinks()29 await Promise.all(sinks.map(runNode))30 return { success: true, results: Object.fromEntries(results) }31}
1// Cron scheduler — distributed lock prevents double-execution2class WorkflowScheduler {3 private readonly lock: RedisLock45 constructor(redis: Redis) {6 this.lock = new RedisLock(redis, { ttl: 30_000 })7 }89 async tick(): Promise<void> {10 const due = await db.workflows.findDue(new Date())1112 await Promise.allSettled(13 due.map(async (wf) => {14 const acquired = await this.lock.acquire(`wf:${wf.id}`)15 if (!acquired) return // another instance is running it1617 try {18 const run = await executeWorkflow(wf)19 await db.runs.create({ workflowId: wf.id, ...run })20 await db.workflows.updateNextRun(wf.id, wf.schedule)21 } catch (err) {22 await db.workflows.recordFailure(wf.id, String(err))23 if (wf.alertOnFailure) await notifyError(wf, err)24 } finally {25 await this.lock.release(`wf:${wf.id}`)26 }27 })28 )29 }30}
Delivery Roadmap
Discovery
Cadrage métier, collecte contraintes et architecture cible
Build
Implémentation coeur produit et intégrations prioritaires
Scale
Hardening: perf, fiabilité, monitoring et tests de charge
Launch
Go-live encadré, transfert, documentation et KPI de suivi
Phase 01 · Discovery
Phase 02 · Build
Phase 03 · Scale
Phase 04 · Launch
Le problème
La PME avait identifié 12 processus répétitifs qui mobilisaient collectivement 28h par semaine : relances fournisseurs à J+5 et J+10, alertes de rupture de stock par email et SMS, rapports hebdomadaires envoyés chaque vendredi soir, notifications à l'équipe terrain lors d'un changement de statut commande... Chacune de ces automatisations nécessitait de faire appel à un prestataire externe, avec 3 à 5 semaines de délai et une facture à chaque modification. L'équipe opérationnelle ne pouvait rien gérer elle-même.
La solution
Builder de workflows visuels drag & drop développé sur React Flow, avec un moteur d'exécution distribué connecté à l'API n8n. Nœuds disponibles : triggers (cron, webhook entrant, email entrant, changement de valeur BDD), actions (Slack, Gmail, Google Sheets, API interne, SMS via Twilio), conditions et branches, délais configurables. Exécution 24/7 avec logs par run, alertes d'erreur automatiques, et accès self-service pour les opérationnels sans aucune compétence technique.
Défis techniques
Persistance de l'état de l'éditeur React Flow avec des connexions complexes (branches conditionnelles, plusieurs outputs par nœud) : sérialisation custom du graphe au format JSON canonique avec validation des cycles (DAG) avant chaque sauvegarde
Verrou distribué Redis pour l'orchestration : deux instances du scheduler pouvaient déclencher le même cron simultanément. Solution : RedisLock avec TTL de 30s et try-finally pour garantir la libération même en cas d'erreur dans le workflow
UI accessible aux non-techs : les termes "webhook", "cron" et "payload" ont été remplacés par "déclenchement url", "planification" et "données reçues" — avec tooltip explicatif sur chaque nœud, testé avec 3 opérationnels sans background IT
Retour client
Avant il fallait appeler le prestataire, expliquer le besoin, attendre le devis, attendre la livraison, tester... 4 semaines minimum pour un changement simple. Maintenant mon responsable ops modifie lui-même les workflows. Les 28 heures qu'on a récupérées, on les a mises sur des clients.
Nom modifié pour respecter le NDA du client.
Impact clé
Stack technique