Skip to content

Application Lifecycle

Cullinan's lifecycle is now usually driven by the active Application, which assembles and owns an ApplicationContext plus a WebRuntime.

Main phases

  1. Discover modulesApplication.run() collects the root module graph and owned packages
  2. Assemble runtime — create an ApplicationContext and WebRuntime candidate
  3. Validate and warm — run health checks, refresh(), router/dispatcher wiring, and warmup hooks
  4. Activate — atomically switch the active runtime and begin draining the previous one
  5. Serve requests — request scope and middleware operate against the request-bound application snapshot
  6. Drain and close — once in-flight requests finish, the old context shuts down and the runtime closes

Lifecycle hooks

Managed components may implement:

  • on_post_construct()
  • on_startup()
  • on_shutdown()
  • on_pre_destroy()

Async variants are supported through _async suffixes.

Ordering

If a component must start or stop before others, implement get_phase(). Lower phases execute earlier on startup and later on shutdown.

Request scope

Request-scoped dependencies are resolved against the current request context. Adapters bind the active runtime into that request context before dispatch and release it afterward. During runtime replacement, Application.current() keeps resolving the request-bound snapshot until that request finishes.

Reload and draining

Application.reload() builds a fresh candidate runtime and activates it only after validation and warmup succeed. The previous runtime moves to DRAINING, keeps serving in-flight requests, and closes only after request counts reach zero.

Middleware bridge

Application bootstrap can bridge older middleware registrations into the gateway pipeline so legacy modules continue to participate in request processing while new code uses the unified Web Runtime.

See also