Application Lifecycle
The Application class is the heartbeat of a Lexigram project. It acts as the Composition Root, the single place where dependencies are wired, providers are registered, and the application’s runtime environment is established.
1. The Composition Root
Section titled “1. The Composition Root”Every Lexigram application begins with a single entry point, typically main.py or app.py. This is where you create the Application instance and configure its initial state.
from lexigram import Application, LexigramConfig
# 1. Initialize Configurationconfig = LexigramConfig.from_yaml()
# 2. Create the Application (Composition Root)app = Application(name="order-service", config=config)
# 3. Add Modules and Providersapp.add_module(MyModule)app.add_provider(MyProvider())
# 4. Boot and runawait app.start()2. Lifecycle Stages
Section titled “2. Lifecycle Stages”sequenceDiagram
participant App as Application
participant P1 as Provider A (Infra)
participant P2 as Provider B (Domain)
Note over App: Phase 1: Registration
App->>P1: register(container)
App->>P2: register(container)
Note over App: Phase 2: Boot
App->>P1: boot(container)
App->>P2: boot(container)
Note over App: Phase 3: Runtime
App->>App: Run (HTTP/CLI/Worker)
Note over App: Phase 4: Shutdown
App->>P2: shutdown()
App->>P1: shutdown()
Phase 1: Registration
Section titled “Phase 1: Registration”In this phase, the application iterates through all added providers and calls their register() method.
- Goal: Bind protocols to implementations in the DI container.
- Rule: Do NOT perform any I/O or side-effects (like connecting to a DB) during registration.
Phase 2: Boot
Section titled “Phase 2: Boot”Once all services are registered, the application enters the Boot phase by calling boot() on every provider.
- Goal: Initialize connections, start background workers, and warm caches.
- Ordering: Providers are booted based on their
priority(e.g.,ProviderPriority.INFRASTRUCTUREboots beforeProviderPriority.DOMAIN).
Phase 3: Runtime
Section titled “Phase 3: Runtime”The application serves traffic — handling HTTP requests, running CLI commands, or processing background tasks.
Phase 4: Shutdown
Section titled “Phase 4: Shutdown”When the application receives a termination signal (SIGINT/SIGTERM), it gracefully shuts down.
- Goal: Close database pools, flush logs, and disconnect from brokers.
- Order: Providers shut down in reverse priority order.
3. Application States
Section titled “3. 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 | Application initialized, no providers booted |
STARTING | Boot in progress |
RUNNING | Application is operational |
STOPPING | Shutdown in progress |
STOPPED | All resources released |
4. Context Manager Form
Section titled “4. Context Manager Form”For scripts or tests, use the context manager form which guarantees shutdown:
import asynciofrom lexigram import Application
async def main(): async with Application.boot( name="my-app", providers=[MyProvider()], modules=[MyModule()], ) as app: # Application is started and running print(f"App state: {app.state}") # AppState.RUNNING # Shutdown happens automatically on exit
asyncio.run(main())5. Health Checks
Section titled “5. Health Checks”Lexigram provides aggregated health checks for monitoring:
# Liveness — is the app alive?liveness = await app.liveness()
# Readiness — is the app ready to serve traffic?readiness = await app.readiness()
# Startup — has the app completed startup?startup = await app.startup_check()
# Full health reporthealth = await app.health_check()print(f"Status: {health.status}")6. Running the Application
Section titled “6. Running the Application”Long-Running (ASGI)
Section titled “Long-Running (ASGI)”# Use as ASGI app with uvicorn# uvicorn my_app.app:app --host 0.0.0.0 --port 8000
# Application auto-starts on first request when used as ASGIapp = Application(name="my-app")app.add_provider(WebProvider())await app.start()Using run_application
Section titled “Using run_application”from lexigram import Application, run_application
app = Application(name="my-app")app.add_provider(MyProvider())
await run_application(app)7. Module Compilation
Section titled “7. Module Compilation”When modules are registered, Lexigram uses ModuleCompiler to:
- Validate import/export visibility
- Detect circular dependencies
- Order providers correctly
# If modules are registered, ModuleCompiler extracts and orders providersif self._modules: from lexigram.di.module.compiler import ModuleCompiler compiler = ModuleCompiler() graph = compiler.compile(root_modules=self._modules, standalone_providers=standalone)8. Events
Section titled “8. Events”Application lifecycle events are emitted via EventBusProtocol:
| Event | When |
|---|---|
ApplicationStarting | Before registration begins |
ApplicationStarted | After all providers booted |
ApplicationStopping | Before shutdown begins |
ApplicationStopped | After all providers shut down |
Subscribers can listen using @event_handler from lexigram.events.