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.
from oridecon import Application, OrideconConfigfrom 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).
Lifecycle stages
Section titled “Lifecycle stages”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 ▼RUNNINGawait app.stop() then:
RUNNING ├─ STOPPING ├─ on_module_shutdown / on_before_shutdown hooks ├─ shutdown() in reverse boot order ├─ container.dispose() ▼STOPPEDIf start() fails, already-booted providers are rolled back and the app lands in STOPPED.
The detailed register / freeze / boot contract is on Boot Sequence.
Application states
Section titled “Application states”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 --> [*]
| State | Description |
|---|---|
CREATED | Constructed. You may still add_module / add_provider. |
STARTING | Boot in progress. |
RUNNING | Serving. is_running is True. |
STOPPING | Shutdown in progress. |
STOPPED | Resources released. start() cannot be called again. |
add_module() and add_provider() raise RuntimeError once the state leaves CREATED.
Context manager
Section titled “Context manager”Application.boot() is a classmethod. It constructs the app, starts it, yields it, and always stops it:
import asynciofrom 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.
Health checks
Section titled “Health checks”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 / UNKNOWNstartup_check() reports unavailable unless the app is RUNNING.
Running the application
Section titled “Running the application”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.
oridecon run # auto-detects my_app.app:app# Optional: an ASGI server you already runuvicorn my_app.app:app --host 0.0.0.0 --port 8000Workers / CLIs (no HTTP)
Section titled “Workers / CLIs (no HTTP)”import asynciofrom oridecon import run_applicationfrom my_app.app import create_app
asyncio.run(run_application(create_app()))run_application starts the app, waits for SIGINT/SIGTERM, then stops.
Module compilation
Section titled “Module compilation”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.
What boot logs
Section titled “What boot logs”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.
Next steps
Section titled “Next steps”- Boot Sequence — register vs boot, freeze, rollback, common mistakes
- Providers — the two-phase contract
- Modules — import/export boundaries
- Project Structure — where
app.pylives