Skip to content
Packages Examples Agents Blog Get started

Application Lifecycle

The Application class is the composition root — the single place providers and modules are wired before anything boots.

The composition root file is always src/<app>/app.py. oridecon run / oridecon dev boot <app>.app:app. See Project Structure.

src/my_app/app.py
from oridecon import Application, OrideconConfig
from oridecon.web import WebModule
def create_app(config: OrideconConfig | None = None) -> Application:
application = Application(name="my-app", config=config)
application.add_modules(
[
WebModule.configure(
discover=["my_app.controllers", "my_app.modules"],
),
]
)
return application
app = create_app()

If you omit config, Application loads OrideconConfig.from_env_profile() (application.yaml plus ORI_PROFILE overlay and ORI_* env vars).


await app.start() runs this sequence. Providers at the same dependency level run in parallel via asyncio.gather().

CREATED
│ config validated for the active environment
│ modules compiled (if any) via ModuleCompiler
├─ register() — bind into the container (no I/O)
├─ freeze() — no further singleton/transient/scoped
├─ validate() — missing deps, cycles, module exports
├─ boot() — resolve, connect, warm caches
RUNNING

await app.stop() then:

RUNNING
├─ STOPPING
├─ on_module_shutdown / on_before_shutdown hooks
├─ shutdown() in reverse boot order
├─ container.dispose()
STOPPED

If start() fails, already-booted providers are rolled back and the app lands in STOPPED.

The detailed register / freeze / boot contract is on Boot Sequence.


stateDiagram-v2
    [*] --> CREATED: Application()
    CREATED --> STARTING: app.start()
    STARTING --> RUNNING: All providers booted
    STARTING --> STOPPED: Boot failed
    RUNNING --> STOPPING: app.stop() or signal
    STOPPING --> STOPPED: All providers shut down
    STOPPED --> [*]
StateDescription
CREATEDConstructed. You may still add_module / add_provider.
STARTINGBoot in progress.
RUNNINGServing. is_running is True.
STOPPINGShutdown in progress.
STOPPEDResources released. start() cannot be called again.

add_module() and add_provider() raise RuntimeError once the state leaves CREATED.


Application.boot() is a classmethod. It constructs the app, starts it, yields it, and always stops it:

import asyncio
from oridecon import Application
async def main() -> None:
async with Application.boot(
name="my-app",
providers=[MyProvider()],
modules=[MyModule],
) as app:
print(app.state) # AppState.RUNNING
asyncio.run(main())

Pass module classes (or DynamicModule from configure()), not MyModule() instances.


These return an AggregateHealthResult (status is the worst component: unhealthy > degraded > healthy):

liveness = await app.liveness()
readiness = await app.readiness()
startup = await app.startup_check()
health = await app.health_check()
print(health.status) # HealthStatus.HEALTHY / DEGRADED / UNHEALTHY / UNKNOWN

startup_check() reports unavailable unless the app is RUNNING.


Application is an ASGI callable. On the first HTTP request it auto-starts if still CREATED. Prefer an explicit lifespan (oridecon run, or an ASGI server you already operate) so boot happens before traffic.

Terminal window
oridecon run # auto-detects my_app.app:app
# Optional: an ASGI server you already run
uvicorn my_app.app:app --host 0.0.0.0 --port 8000
import asyncio
from oridecon import run_application
from my_app.app import create_app
asyncio.run(run_application(create_app()))

run_application starts the app, waits for SIGINT/SIGTERM, then stops.


When _modules is non-empty, start() runs ModuleCompiler before any register():

from oridecon.di.module import ModuleCompiler
compiler = ModuleCompiler()
graph = compiler.compile(
root_modules=self._modules,
standalone_providers=standalone,
)

The compiler’s six phases: collect → cycle detection → validation → re-export expansion → visibility → provider ordering. Standalone add_provider() entries are merged into that plan.

If config.discovery.auto_discover is True, discover_modules() runs first.


Application.start() / stop() emit structured log events (application.starting, application.started, application.stopping, application.stopped) and an optional startup banner (ORI_QUIET=1 silences it).

Typed dataclasses also exist at oridecon.app.events (ApplicationStarting, ApplicationStarted, ApplicationStopping, ApplicationStopped). Boot does not publish them onto EventBusProtocol — that bus lives in oridecon-events and is only present if you add it.