44 lines
2.1 KiB
Markdown
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.
|