Skip to content

Latest commit

 

History

History
117 lines (93 loc) · 3.15 KB

File metadata and controls

117 lines (93 loc) · 3.15 KB

lifecycle

Tier: Platform · Status: Full · Java original: SpringApplication.run() · .NET project: IHost + IHostedService

Overview

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) }

Why a separate module?

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.

Public surface

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) error

Lifecycle diagram

Run(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)

Quick start

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.

Testing

cd lifecycle
go test ./...

Covers ordered start + reverse-ordered stop, start-hook failure rollback, HTTP server lifecycle (start + drain), and joined stop-hook errors.