Runtime Lifecycle
The runtime lifecycle is the ordered path from app construction to startup, command execution, runtime work, and graceful shutdown.
GoForj keeps this path explicit. Startup and shutdown behavior belongs in documented hooks, not package globals or hidden runtime registration.
Start Here
Use lifecycle hooks when app behavior must run at startup or shutdown with injected dependencies.
Default app:
app/lifecycle.goNamed app:
app/marketplace/lifecycle.goDo not use lifecycle hooks for ordinary request, job, schedule, command, or constructor work.
Execution Flow
An app command follows this shape:
- load environment configuration
- initialize the app through its Wire graph
- parse the selected command
- start lifecycle phases
- run the command or runtime
- shut down with bounded timeouts
flowchart LR entry["cmd/<app>/main.go"] --> wire["app/<app>/wire"] wire --> startup["BeforeStartup -> Startup -> AfterStartup"] startup --> command["run command or runtime"] command --> shutdown["BeforeShutdown -> Shutdown -> AfterShutdown"]
Lifecycle Support
Reusable lifecycle machinery lives in:
internal/runtimeGenerated app metadata lives in:
internal/runtime/apps.goApp owners should edit app/lifecycle.go, not internal/runtime.
Register Hooks
Example:
package app
type LifecycleRegistry struct {
reports *reports.Service
}
func NewLifecycleRegistry(reports *reports.Service) *LifecycleRegistry {
return &LifecycleRegistry{reports: reports}
}
func (r *LifecycleRegistry) Startup(ctx context.Context) error {
return r.reports.WarmCache(ctx)
}
func (r *LifecycleRegistry) Shutdown(ctx context.Context) error {
return r.reports.Flush(ctx)
}NewLifecycleRegistry is built by Wire, so it can receive services and repositories.
Runtime Boundaries
The lifecycle applies to generated commands, but commands do different work:
forj route:liststarts, lists routes, and shuts down.forj apistarts the HTTP runtime and blocks.forj workerstarts workers and blocks.forj schedulerstarts scheduler work and blocks.forj appstarts enabled runtimes together.
Named apps use the same shape:
forj marketplace api
forj marketplace workerCommon Mistakes
Common mistakes
- Do not put startup behavior in
cmd/<app>/main.go. - Do not put app-specific startup behavior in
internal/runtime. - Do not make required dependencies appear optional.
- Do not start long-lived runtime work from constructors.
Next Steps
- Runtime Topology explains app and runtime process shapes.
- Project Structure explains where runtime packages live.
