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

MethodDescription
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
})
ConstantEvent namePayloadWhen
ProviderRegisterprovider:registernilAll providers registered
PluginRegisterplugin:registernilAll plugins registered + bindings
ProviderBootprovider:bootnilAll providers booted
PluginBootplugin:bootnilAll plugins booted
RouteRegisteredroute:registerednilPlugin routes mounted
MiddlewareRegisteredmiddleware:registerednilPlugin middleware merged
AppBootedapp:bootednilBoot complete, all capabilities applied
AppStartedapp:startedstring (port)Server is listening
AppShutdownapp:shutdownos.SignalGraceful shutdown started
DatabaseQuerydb:queryQueryPayloadAfter any query (SELECT)
DatabaseInsertdb:insertQueryPayloadAfter INSERT
DatabaseUpdatedb:updateQueryPayloadAfter UPDATE
DatabaseDeletedb:deleteQueryPayloadAfter 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"