Events
An Event is a typed fact that something happened.
Use events for fan-out and decoupled reactions. Use queues and jobs for durable background work, retries, delays, timeouts, and worker lifecycle.
Events library reference
This guide follows events through a GoForj App's publishers, subscribers, and configuration. The events library page contains the standalone usage, complete package reference, and distributed-driver matrix.
When to Use Events
Use an event when something happened and one or more subscribers may react to that fact. Start with inproc for same-process fan-out. Choose a transport-backed driver when subscribers must run in other processes or hosts.
Use a queue instead when the work needs durability, retries, delays, timeout policy, queue selection, or independently scaled workers.
Generate an Event
Create an event type for the default App:
forj make:event UserRegisteredUse a grouped name to colocate an event with its owning package:
forj make:event billing:invoice-paidCreate event subscribers separately with the Event Subscribers workflow. Subscribers add App-owned wiring and can target a named bus with --bus <name>.
Use domain.past_tense topics, such as users.created or invoices.paid. Review the generated topic constant before other code depends on it. See Naming Conventions for the full naming map.
Add the Payload
Replace the generated placeholder with the fact subscribers need. The scaffold derives billing.invoice-paid from the grouped name; choose the stable domain topic before publishers or subscribers depend on it:
// UserRegisteredEvent carries the user registration fact.
type UserRegisteredEvent struct {
UserID string `json:"user_id"`
}
// Topic returns the stable user registration topic.
func (UserRegisteredEvent) Topic() string {
return "users.registered"
}Topics should be stable when other code or infrastructure depends on them.
Publishing
Publish through the App's generated event bus:
err := app.Bus().WithContext(ctx).Publish(UserRegisteredEvent{
UserID: user.ID,
})In services, prefer injecting the event bus or a small publisher wrapper instead of reaching through global state.
Drivers
Compile-time support:
EVENTS_SUPPORTED_DRIVERS=inproc,redisRuntime selection:
EVENTS_DRIVER=inproc
EVENTS_AUDIT_DRIVER=redisinproc is process-local, non-durable, and needs no transport settings. Use distributed drivers when events need to cross process boundaries. See Environment Reference for each driver's settings.
Verification
Use the in-process Driver in focused tests. Publish a typed event, record what the subscriber receives, and assert the topic and payload. Also test a subscriber error according to the selected Driver's documented behavior; do not turn that error into an assumed retry guarantee.
When moving to a distributed Driver, verify the actual process boundary: start the publisher and subscriber with the release configuration, publish one uniquely identified non-production event, and confirm the intended subscriber observes it exactly as your application requires. Check duplicate handling as well as successful delivery because transport redelivery and subscriber fan-out depend on the Driver.
Readiness can prove that an event backend is reachable. It cannot prove that the remote subscription uses the intended topic, credentials, or consumer topology.
Regeneration
After changing supported drivers or named event buses, use the normal build path:
forj buildDuring forj dev, an app listed in dev.apps rebuilds automatically. Generation Commands covers focused maintainer workflows.
Next Steps
- Event Subscribers explains handlers.
- Events versus Queues explains boundary decisions.
- Environment Reference lists driver settings.
- Naming Conventions defines stable event topics.
make:eventReference lists generation, removal, and shared options.- Events covers standalone package details.