Tier: Platform · Status: Full · Java original:
SpringApplication.run()· .NET project:IHost+IHostedService
lifecycle provides the canonical application orchestrator every
Firefly Go service uses. It owns:
- Ordered start hooks (
OnStart). - HTTP servers (
OnHTTP) — multiple ports allowed, each gets its own goroutine. - Reverse-ordered stop hooks (
OnStop). - Signal trap (default
SIGINT+SIGTERM). - Drain budget (default 30 s) granted to stop hooks + HTTP shutdown.
- Failure rollback — a start hook error triggers stop hooks for the hooks that already started.
app := lifecycle.New("orders").
OnStart(broker.Start).
OnStart(scheduler.Start).
OnHTTP(":8080", coreMiddleware(apiMux)).
OnHTTP(":8081", actuatorMux).
OnStop(broker.Stop).
OnStop(scheduler.Stop)
if err := app.Run(ctx); err != nil { log.Fatal(err) }The Spring Boot SpringApplication.run() line is famously concise
because the framework owns the entire lifecycle. Idiomatic Go
typically scatters this across main.go (signal handler, server.Shutdown,
defer cleanup, drain timeout). lifecycle lifts that into one
declarative composition so every service handles SIGTERM the same way
on day one.
type Hook func(ctx context.Context) error
type HTTPServer struct {
Addr string
Handler http.Handler
Server *http.Server
}
const DrainTimeout = 30 * time.Second
type Application struct { ... }
func New(name string) *Application
func (*Application) WithLogger(*slog.Logger) *Application
func (*Application) WithDrainTimeout(time.Duration) *Application
func (*Application) OnStart(Hook) *Application
func (*Application) OnStop(Hook) *Application
func (*Application) OnHTTP(addr string, handler http.Handler) *Application
func (*Application) Run(ctx context.Context) errorRun(ctx)
│
├─ for each OnStart: start hook (in registration order)
│ │
│ ├─ on error: rollback start by running OnStop hooks; return joined err
│
├─ for each OnHTTP: spawn ListenAndServe goroutine
│
├─ block on:
│ │ ctx.Done()
│ │ signal (SIGINT / SIGTERM by default)
│ │ http server failure
│
├─ derive drainCtx with DrainTimeout
├─ shutdown HTTP servers
├─ run OnStop hooks in REVERSE order
└─ return joined errors (trigger ⊕ http ⊕ stop)
import (
"context"
"os/signal"
"syscall"
"github.com/fireflyframework/fireflyframework-go/lifecycle"
)
func main() {
app := lifecycle.New("orders").OnHTTP(":8080", mux)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
if err := app.Run(ctx); err != nil { log.Fatal(err) }
}startercore.Core.NewApplication() returns an Application
pre-configured with the Core's logger.
cd lifecycle
go test ./...Covers ordered start + reverse-ordered stop, start-hook failure rollback, HTTP server lifecycle (start + drain), and joined stop-hook errors.