Émettre un événement métier¶
Ce guide s'adresse aux auteurs de modules. Il décrit l'enveloppe à publier, la convention de nommage des types, et la manière de l'émettre depuis un module Java.
Le principe¶
Un module qui vient d'écrire quelque chose de notable publie une enveloppe sur le sujet logs d'Apache Pulsar. Il ne s'adresse pas au module Journal d'événements, ne l'attend pas, et ne sait pas ce qu'il en fait. Le journal consomme ce sujet pour son propre compte.
Un événement décrit un fait accompli. On l'émet donc après l'écriture, jamais avant, et son verbe est au passé.
L'enveloppe¶
L'enveloppe est un objet JSON :
{
"specversion": "1",
"id": "6f1c8e2a-9b34-4d51-8a7e-2c3f5b1d0e77",
"time": "2026-08-23T09:41:12Z",
"source": "stocks",
"type": "stocks.inventory.validated",
"organization_id": "c81b0d44-2f5a-4a19-9d7c-8e0a6b3f21ce",
"actor": {
"id": "3a7e1f90-5c26-4b83-a1d4-9f0e7c2b6d15",
"name": "Awa Koné"
},
"subject": {
"type": "inventory",
"id": "1d9c4b7e-8a05-4f62-b3e1-7c25a0f9d834",
"label": "INV-2026-0042"
},
"level": "INFO",
"correlation_id": "b2f4a681-3d70-49ce-8b52-1a6e0c94f7d3",
"data": {
"warehouse": "Entrepôt principal",
"lines": 17
}
}
| Champ | Obligatoire | Rôle |
|---|---|---|
specversion |
Oui | Version de l'enveloppe, "1" |
id |
Oui | UUID de l'événement, employé pour la déduplication |
time |
Oui | Instant du fait, ISO 8601 en UTC |
source |
Oui | Module émetteur, identique au premier segment de type |
type |
Oui | Nature du fait, selon la convention ci-dessous |
organization_id |
Oui | Organisation à laquelle le fait appartient |
actor |
Oui | Auteur du fait : son identifiant et son nom |
subject |
Oui | Entité concernée : son type, son identifiant et son libellé |
level |
Oui | INFO, WARN, SECURITY ou ERROR |
correlation_id |
Non | Relie plusieurs événements issus d'un même traitement |
data |
Oui | Données métier libres, objet JSON de 16 KiB au plus |
L'enveloppe entière ne dépasse pas 32 KiB, dont 16 KiB au plus pour data. Au-delà, le message part en lettre morte plutôt que d'être tronqué.
Les libellés sont dénormalisés à l'émission
subject.label et actor.name sont recopiés dans l'enveloppe au moment de l'émission, et jamais relus ensuite. Un événement dit donc ce que valait le libellé ce jour-là : renommer un article plus tard ne réécrit pas l'histoire. C'est voulu.
La convention de nommage¶
Un type se lit <source>.<entité>.<verbe-au-passé>, en ASCII minuscule :
[a-z0-9_]+(\.[a-z0-9_]+){2}
Trois segments exactement, séparés par des points. Le premier est le nom du module et doit être égal au champ source. Le deuxième nomme l'entité au singulier. Le troisième est le verbe, au participe passé.
Les verbes recommandés, à préférer à toute invention :
created, updated, deleted, validated, cancelled, executed, closed, archived, imported, exported
| Correct | Incorrect | Pourquoi |
|---|---|---|
stocks.inventory.validated |
stocks.validate_inventory |
Deux segments, et un verbe à l'infinitif |
sales.cash_receipt.closed |
sales.cashReceipt.closed |
Le camelCase n'entre pas dans le motif |
stocks.article.updated |
stock.article.updated |
La source est le module stocks |
Le niveau¶
INFO est le cas par défaut. On ne s'en écarte que pour une raison :
WARN— un fait qui mérite l'attention sans être une fauteSECURITY— une connexion, une permission refusée, un export de donnéesERROR— un échec métier notable
L'acteur système¶
Un événement émis hors d'une requête HTTP — traitement planifié, consommation d'un message, migration — n'a pas d'utilisateur derrière lui. Il emploie alors l'acteur système :
actor.id:00000000-0000-0000-0000-000000000000actor.name: le nom du module
En Java¶
La bibliothèque com.minlessika.erp:business-events, paquet com.minlessika.events, porte l'enveloppe et sa publication.
final Envelope envelope = new Envelope(
new Occurrence(
new Stamp(),
new EventType("stocks.inventory.validated"),
orgId
),
new Actor(userId, "Awa Koné"),
new Subject("inventory", id, "INV-2026-0042"),
new Details(Level.INFO, data)
);
broadcast.emitLater(new BusinessEventMessage(envelope));
Stamp fixe l'identifiant et l'instant de l'événement. emitLater diffère la publication : le message est écoulé après le commit de la transaction, et purgé si elle est annulée. Un fait qui n'a pas eu lieu n'est donc jamais journalisé.
L'appel se place après l'écriture métier, dans la même transaction qu'elle.
Chaque module enregistre LogsPublishing une fois, au démarrage, pour disposer de broadcast.
Une fois émis¶
Le module Journal d'événements consomme le sujet logs et range les enveloppes dans une table events partitionnée par mois. La déduplication se fait sur id : republier deux fois la même enveloppe ne crée pas deux lignes.
Une enveloppe que le consommateur ne sait pas lire part vers logs-DLQ et devient visible dans l'écran Lettres mortes. Un type hors motif ou une enveloppe trop grosse s'y retrouvent : c'est là qu'il faut regarder quand un événement attendu n'apparaît pas dans le journal.
Pensez enfin à déclarer votre nouvel événement dans le Catalogue des événements.
Changelog¶
- 1.0 (23 août 2026) : création du document.