Skip to content

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:

bash
forj make:event UserRegistered

Use a grouped name to colocate an event with its owning package:

bash
forj make:event billing:invoice-paid

Create 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:

go
// 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:

go
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:

text
EVENTS_SUPPORTED_DRIVERS=inproc,redis

Runtime selection:

text
EVENTS_DRIVER=inproc
EVENTS_AUDIT_DRIVER=redis

inproc 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:

bash
forj build

During forj dev, an app listed in dev.apps rebuilds automatically. Generation Commands covers focused maintainer workflows.

Next Steps ​