Publishing events
Modules collaborate without knowing each other: they publish events to the core, which relays them (LISTEN/NOTIFY) and logs them.
Publishing #
// events.rs — publication best-effort
pub async fn publish_note_created(state: &AppState, note_id: Uuid, user_id: Uuid) {
let payload = json!({
"type": "NoteCreated",
"payload": { "note_id": note_id, "user_id": user_id, "module_id": "memo" }
});
let url = format!("{}/internal/events/publish", state.settings.core.url);
let _ = reqwest::Client::new().post(&url)
.header("X-Internal-Secret", &state.settings.core.internal_secret)
.json(&payload).send().await;
}Publishing is fire-and-forget so the user request is never slowed down:
// dans le handler create, après la réponse — on n'attend pas la publication
let st = state.clone();
tokio::spawn(async move { events::publish_note_created(&st, note.id, user.id).await; });Publishing — in other languages #
// Rust
let payload = json!({ "type": "NoteCreated",
"payload": { "note_id": note_id, "user_id": user_id, "module_id": "memo" } });
client.post(format!("{core}/internal/events/publish"))
.header("X-Internal-Secret", secret)
.json(&payload).send().await.ok();<?php
$ch = curl_init("$core/internal/events/publish");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-Internal-Secret: $secret", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"type" => "NoteCreated",
"payload" => ["note_id" => $noteId, "user_id" => $userId, "module_id" => "memo"],
]),
]);
curl_exec($ch);# Python — requests
requests.post(f"{core}/internal/events/publish",
headers={"X-Internal-Secret": secret},
json={"type": "NoteCreated",
"payload": {"note_id": note_id, "user_id": user_id, "module_id": "memo"}})// Go
body, _ := json.Marshal(map[string]any{"type": "NoteCreated",
"payload": map[string]string{"note_id": noteID, "user_id": userID, "module_id": "memo"}})
req, _ := http.NewRequest("POST", core+"/internal/events/publish", bytes.NewReader(body))
req.Header.Set("X-Internal-Secret", secret)
http.DefaultClient.Do(req)# Perl — LWP
$ua->post("$core/internal/events/publish",
'X-Internal-Secret' => $secret, 'Content-Type' => 'application/json',
Content => encode_json({ type => "NoteCreated",
payload => { note_id => $note_id, user_id => $user_id, module_id => "memo" } }));Subscribing #
Declare your subscriptions in the manifest ([events].subscribed) — for instance UserDeleted to purge the notes of a deleted user. This is how consistency is kept across modules without any code coupling.
Note
Emit events in the past tense with clear names (NoteCreated, NoteDeleted); always include module_id in the payload.