Files
launcher/docs/adr/ADR-003-before-start-hooks.md
Rene Nochebuena f2e3faa1d6 feat(launcher): initial stable release v0.9.0
Application lifecycle manager enforcing a three-phase init/wire/start sequence with reverse-order graceful shutdown and per-component stop timeouts.

What's included:
- `Component` interface (OnInit / OnStart / OnStop) and `Hook` type for BeforeStart wiring functions
- `Launcher` interface with Append, BeforeStart, Run (blocks on SIGINT/SIGTERM), and idempotent Shutdown(ctx)
- `New(logger, opts...)` constructor with configurable ComponentStopTimeout (default 15 s); no global state

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-18 23:49:12 +00:00

44 lines
2.1 KiB
Markdown

# ADR-003: BeforeStart Hooks for Dependency Injection Wiring
**Status:** Accepted
**Date:** 2026-03-18
## Context
After all components have been initialised (`OnInit`), some wiring cannot be
expressed at construction time because it requires the initialised state of multiple
components simultaneously. For example, an HTTP server may need to register routes
that reference handler functions which themselves hold references to a database
client — but the database client only becomes ready after `OnInit` runs, which is
after all components are constructed.
One alternative is to wire everything in `main` before calling `Run`, but that
requires `main` to know the internal structure of every component, defeating
encapsulation. Another alternative is to wire in `OnStart`, but at that point other
components may already be running and the window for setup errors is narrower.
## Decision
`Launcher.BeforeStart(hooks ...Hook)` registers functions of type `func() error`
that are called after all `OnInit` calls succeed and before any `OnStart` call
begins. Hooks are called in registration order. If any hook returns an error, `Run`
returns that error immediately without proceeding to `OnStart`.
`Hook` is a plain function type with no parameters beyond what closures capture.
This allows hooks to close over the components they wire, without `launcher` needing
to know anything about those components beyond the `Component` interface.
## Consequences
- Dependency injection wiring is expressed as closures registered with
`BeforeStart`, keeping `main` as the composition root without exposing internals.
- All `OnInit` guarantees (connections open, ports bound, resources allocated) are
satisfied before any hook runs — hooks can safely call methods on initialised
components.
- Hook errors abort the lifecycle cleanly. No components have started yet, so no
cleanup of running services is needed (though `OnStop` will still run for any
components that successfully ran `OnInit`, consistent with ADR-001's behaviour
when `OnStart` fails).
- The pattern scales to any number of wiring steps without adding methods to the
`Component` interface.