Events
Nimbus includes a lightweight pub/sub event dispatcher for decoupling application components. Fire events from controllers, listen in plugins, and react without tight coupling.
Overview
The event system is available on app.Events and as package-level helpers in events. Listeners are functions that receive a payload and return an error.
Listening for events
import "github.com/CodeSyncr/nimbus/events"
// Using the app dispatcher
app.Events.Listen("user.created", func(payload any) error {
user := payload.(*models.User)
return sendWelcomeEmail(user)
})
// Using package-level helpers (global dispatcher)
events.Listen("order.placed", func(payload any) error {
order := payload.(*models.Order)
return notifyWarehouse(order)
})
// Multiple listeners on the same event
app.Events.Listen("user.created", auditLogger)
app.Events.Listen("user.created", sendSlackNotification)
Dispatching events
// Synchronous — all listeners run in order, returns first error
err := app.Events.Dispatch("user.created", user)
// Asynchronous — each listener runs in its own goroutine, errors are logged
app.Events.DispatchAsync("analytics.track", trackData)
// Package-level helpers
events.Dispatch("order.placed", order)
events.DispatchAsync("notification.send", msg)
Event listeners in plugins
Plugins can declare listeners via the HasEvents capability interface. They are automatically registered during boot.
func (p *AuditPlugin) Listeners() map[string][]events.Listener {
return map[string][]events.Listener{
"user.created": {p.onUserCreated},
"user.deleted": {p.onUserDeleted},
"order.placed": {p.onOrderPlaced},
}
}
func (p *AuditPlugin) onUserCreated(payload any) error {
user := payload.(*models.User)
return p.log("user.created", user.ID)
}
Dispatcher API
| Method | Description |
|---|---|
Listen(event, fn) | Register a listener |
Dispatch(event, payload) | Fire synchronously, returns first error |
DispatchAsync(event, payload) | Fire asynchronously in goroutines |
Has(event) | Check if event has listeners |
ListenerCount(event) | Number of registered listeners |
Clear(events...) | Remove listeners (all or specific events) |
Built-in framework events
Nimbus dispatches these events automatically at each lifecycle stage. Use the constants from events:
import "github.com/CodeSyncr/nimbus/events"
app.Events.Listen(events.AppBooted, func(payload any) error {
log.Println("App is ready!")
return nil
})
app.Events.Listen(events.AppShutdown, func(payload any) error {
sig := payload.(os.Signal)
log.Println("Shutting down due to:", sig)
return nil
})
| Constant | Event name | Payload | When |
|---|---|---|---|
ProviderRegister | provider:register | nil | All providers registered |
PluginRegister | plugin:register | nil | All plugins registered + bindings |
ProviderBoot | provider:boot | nil | All providers booted |
PluginBoot | plugin:boot | nil | All plugins booted |
RouteRegistered | route:registered | nil | Plugin routes mounted |
MiddlewareRegistered | middleware:registered | nil | Plugin middleware merged |
AppBooted | app:booted | nil | Boot complete, all capabilities applied |
AppStarted | app:started | string (port) | Server is listening |
AppShutdown | app:shutdown | os.Signal | Graceful shutdown started |
DatabaseQuery | db:query | QueryPayload | After any query (SELECT) |
DatabaseInsert | db:insert | QueryPayload | After INSERT |
DatabaseUpdate | db:update | QueryPayload | After UPDATE |
DatabaseDelete | db:delete | QueryPayload | After DELETE |
Note: Database events dispatch asynchronously. The QueryPayload contains SQL, Vars, RowsAffected, Duration, and Error.
Custom event naming conventions
Use dot-notation for your own events:
// Auth events
"user.created"
"user.updated"
"user.deleted"
"user.login"
// Business events
"order.placed"
"order.shipped"
"payment.succeeded"
"payment.failed"