dispatchEvent — fire a domain event
Imported from the facade: import { dispatchEvent } from "@/framework/facade.js".
Fans a domain event out to Socket.IO broadcast and/or BullMQ queues in one call. Broadcasts first (when broadcast is given), then enqueues (when queue is given). See Events & Queue.
Signature
| Function | Signature | Description |
|---|---|---|
dispatchEvent | (name, payload, options?) => Promise<void> | Broadcast via Socket.IO and/or enqueue a background job |
Options:
| Option | Type | Description |
|---|---|---|
broadcast | object | Socket.IO audience — all | auth | roles[] | users[] | rooms[] (combinable) |
queue | string | boolean | true → "default" queue; string → named queue |
Broadcast targets:
ts
broadcast: { all: true } // every connected client
broadcast: { auth: true } // all authenticated clients (room "auth")
broadcast: { roles: ["admin"] } // role rooms
broadcast: { users: [1, 42] } // user rooms ("user:1", "user:42")
broadcast: { rooms: ["room:chat:general"] } // custom rooms
broadcast: { roles: ["admin"], users: [userId] } // combine freelyUse cases
Broadcast only
ts
await dispatchEvent(
"post.published",
{ postId: 1, title: "Hello" },
{
broadcast: { all: true },
},
);Queue only
ts
await dispatchEvent(
"user:signup",
{ userId, email },
{
queue: "mail", // "true" uses the "default" queue
},
);Broadcast + queue together
ts
await dispatchEvent("post.publish", body, {
queue: "default",
broadcast: { roles: ["admin"], users: [authorId] },
});Real world — events across the app
The event name doubles as the job name when queueing (dispatchEvent → shouldQueue with the same string):
ts
// modules/auth/controllers/auth.controller.ts — forgot-password flow
await dispatchEvent(
"user:forget-password",
{ email: user.email, forgetPassword: user.forgetPassword },
{ queue: "mail" },
);
// modules/auth/jobs/forgetpass.ts — the matching handler
shouldQueue("user:forget-password", "mail", async (job) => {
await mail.sendMail({
to: job.data.email,
subject: "Forget password",
html: `Your password is ${job.data.forgetPassword}`,
});
return { ok: true };
});Broadcast targets map to real audiences — refresh the authenticated crew after a file change, or ping exactly the user who triggered an export:
ts
// modules/report/controllers/files.controller.ts
await dispatchEvent(
"report.file.changed",
{ name: file.name },
{ broadcast: { auth: true } },
);
// modules/report/jobs/exportJob.ts — tell the requester their export is ready
await dispatchEvent(
"report.export.ready",
{ downloadUrl, authId },
{ broadcast: { users: [authId] } },
);Notes
- The event
nameis used verbatim as the queue job name (shouldQueue(name, ...)) and as the socket event name, so server and client (pulse.channel(x).listen(name)) stay in sync. - Both channels degrade gracefully — with
SOCKET=falsebroadcast is a no-op; with no Redis, queueing returnsnulland never throws.
