Nos applications ont presque toutes la même forme. Elles tournent longtemps. Elles relèvent des flux, enrichissent des données, publient à heure fixe. Et, de plus en plus, un agent IA doit pouvoir leur demander quelque chose. Socle V005 est le framework Java écrit pour cette forme-là. Cet article en donne la carte : cinq mots, un worker réel, et ce que le framework ne fait pas.
Le problème
Dans une application Spring ordinaire, le travail de fond se disperse. Un @Scheduled ici, un thread lancé à la main là, un contrôleur écrit exprès pour déclencher un traitement. Personne ne sait dans quel ordre tout démarre, ni dans quel ordre tout s’arrête. Et le jour où un agent IA doit appeler ces traitements, il faut encore écrire une couche pour lui.
Socle V005 range tout cela sous une seule règle : chaque traitement est un worker, et un seul chef d’orchestre les fait vivre.
Cinq mots pour tout comprendre
Le Worker
Une unité de traitement, un fichier Java. Il a un cycle de vie explicite : initialize,
start, doWork, stop. Il ne se planifie jamais lui-même.
Le MOP
Le Main Orchestrator Process. Il démarre les workers par priorité, appelle leur doWork au bon rythme, surveille leur santé, les redémarre, et les arrête dans l’ordre inverse.
Le KvBus
Le bus interne : des clés, des valeurs, et des sujets auxquels on publie et on s’abonne. En mémoire, ou sur Redis quand plusieurs instances doivent se parler.
L’Action
Une commande qu’un worker déclare une fois. Le framework l’expose en REST et, si MCP est activé, comme outil pour un agent IA. Aucun contrôleur à écrire.
La TechDB
Une base H2 embarquée, purement technique : états des workers, journaux, audit des actions. Les données métier vont ailleurs, en général dans PostgreSQL.
Un worker réel
Voici le moteur de diffusion de TheDiffuseur, en production. Chaque minute, il prend ce qui est dû et l’envoie. Il ne décide de rien d’autre.
@Override
public String getName() {
return "diffusion_worker";
}
@Override
public String getSchedule() {
return "* * * * *";
}
@Override
public void doWork() {
dernierPassage = Instant.now();
List<Long> dues = publications.cellesQuiSontDues(plafondParCycle);
if (dues.isEmpty()) return;
log.info("[worker:{}][step:cycle] {} cible(s) due(s)", getName(), dues.size());
for (Long idCible : dues) {
try {
switch (publications.envoyer(idCible, signature)) {
case PUBLIEE -> publiees.incrementAndGet();
case ECHEC_TEMPORAIRE, ECHEC_DEFINITIF -> echecs.incrementAndGet();
case DEJA_PRISE -> dejaPrises.incrementAndGet();
}
} catch (RuntimeException e) {
// Une cible qui explose ne doit pas emporter les autres : le cycle continue.
echecs.incrementAndGet();
log.error("[worker:{}][step:cible][cible:{}] Échec non rattrapé", getName(), idCible, e);
}
}
}
Trois choses à remarquer :
- Pas de boucle, pas de
Thread.sleep. Le worker dit quand il veut travailler (getSchedule(), une expression cron à cinq champs). C’est le MOP qui l’appelle. - Une erreur ne tue pas le cycle. Une cible qui échoue est comptée ; les autres partent.
- Deux instances ne publient pas deux fois.
envoyerprend chaque cible par une écriture conditionnelle en base. La seconde instance voitDEJA_PRISEet passe à la suivante.
Le même worker déclare deux actions, get_status et forcer_un_cycle. Sans une ligne de plus, elles répondent en REST sur POST /admin/workers/diffusion_worker/actions/forcer_un_cycle, et un agent IA les voit comme l’outil MCP diffusion_worker__forcer_un_cycle.
@Override
public List<ActionMetadata> getActions() {
return List.of(
ActionMetadata.builder()
.name("get_status")
.description("Compteurs du moteur de diffusion")
.mode(ExecutionMode.SYNC)
.readOnly(true)
.build(),
ActionMetadata.builder()
.name("forcer_un_cycle")
.description("Exécute un cycle immédiatement, sans attendre la minute suivante")
.mode(ExecutionMode.SYNC)
.readOnly(false)
.build());
}
Ni Spring Batch, ni Temporal
On nous pose souvent la question. La réponse tient dans un tableau.
| Spring Batch | Temporal | Socle V005 | |
|---|---|---|---|
| Modèle | des jobs découpés en étapes, par chunks | des workflows dont l’historique est répliqué | des workers qui tournent en continu |
| Exécution | déclenchée, planifiée ou à la main | pilotée par le moteur de workflow | continue, et réactive aux événements |
| État | un JobRepository en base |
durable et rejouable | au mieux : TechDB et KvBus, sans rejeu |
| Exploitation | une base à côté de l’application | un cluster Temporal à opérer | un JAR, une JVM |
Si vous devez traiter des milliards de lignes avec reprise sur incident, prenez Spring Batch. Si un processus dure des semaines et doit survivre à tout, avec des tâches humaines, prenez Temporal ou Camunda. Socle vise autre chose : une application vivante, dans une seule JVM, qui réagit, décide et se laisse piloter par un agent.
Ce que Socle n’est pas
- Pas un orchestrateur de conteneurs. Le MOP orchestre des workers dans une JVM, pas des services répartis.
- Pas un ORM. Pas de JPA : du JDBC direct vers PostgreSQL.
- Pas un remplaçant de Spring. Il s’appuie sur Spring Boot pour l’injection et la configuration.
- Pas un framework Kafka. Il sait consommer Kafka, mais ce n’est pas son cœur.
Ce que ça donne en vrai
La version en service est la 5.8.6, sur Java 21 et Spring Boot 4.0.6. Le Hub TheSocle est lui-même une application Socle : 61 workers sur le Hub thesocle.net, relevés le 2026-09-23. La Veille, Entreprises, SmartDailyReach et TheDiffuseur sont construites de la même façon.
Chaque application sert son tableau de bord sur /dashboard, sur son propre port, et sa sonde de santé sur /actuator/health. Il n’y a plus de port dédié au tableau de bord.
Pour un décideur, ce que ça évite
Chaque application que nous livrons a la même ossature. Un développeur qui en connaît une se retrouve dans toutes. Un traitement qui plante ne fait pas tomber les autres, et il redémarre seul. Un agent IA peut interroger ou déclencher un traitement sans qu’on développe une interface pour lui. Vous n’achetez pas un prototype à réécrire : vous achetez un programme qui tient.
À retenir
- Un traitement = un worker ; le MOP seul le fait vivre.
- Une action déclarée une fois sert en REST et en MCP.
- Une JVM, pas un cluster : c’est un choix, avec ses limites.
