Application Lifecycle & Bootstrapping
Relevant source files
The following files were used as context for generating this wiki page:
The Application Lifecycle and Bootstrapping system in Forze manages the structured initialization, runtime execution, and graceful shutdown of backend services. It is designed around the principles of Domain-Driven Design and Hexagonal Architecture, ensuring that infrastructure components like database pools, cache clients, and message brokers are explicitly managed and isolated from the core domain logic.
The bootstrapping process involves composing three distinct execution plans: the DepsRegistry for dependency management, the LifecyclePlan for resource orchestration, and the OperationRegistry for handler pipeline composition. These are combined into an ExecutionRuntime that governs the application's active state and resource cleanup.
Sources: skills/forze-wiring/SKILL.md, CLAUDE.md
Core Runtime Architecture
The ExecutionRuntime serves as the central orchestrator for the application's lifecycle. It requires frozen registries for both dependencies and lifecycle steps to ensure a stable, immutable runtime environment.
Execution Plans
Bootstrapping is categorized into three primary plans:
| Plan | Component | Purpose |
|---|---|---|
| Dependency Plan | DepsRegistry | Manages factories for ports (document, search, etc.) and plain dependencies. |
| Lifecycle Plan | LifecyclePlan | Orchestrates the startup and shutdown sequence of physical resources (connection pools). |
| Operation Plan | OperationRegistry | Defines the pipeline of handlers, transaction scopes, and middleware stages. |
Sources: skills/forze-wiring/SKILL.md:10-15
Runtime Initialization Flow
The following diagram illustrates the transition from static configuration to an active ExecutionRuntime.
The runtime must be used within an async with runtime.scope(): block to provide an active context for handlers.
Sources: skills/forze-wiring/SKILL.md:65-85
Dependency Management & Specs
Forze uses a decoupled approach where logical Specs define "what" is needed, and Deps Modules define "how" those needs are met by infrastructure.
Logical Specs vs. Infrastructure Config
- Specs: (e.g.,
DocumentSpec,SearchSpec) declare model types and a uniquename. They contain no infrastructure-specific details like DSNs or table names. - Deps Modules: (e.g.,
PostgresDepsModule,RedisDepsModule) map the Specnameto physical configuration such as SQL tables, Redis namespaces, or S3 buckets.
Sources: skills/forze-specs-infrastructure/SKILL.md:10-25, skills/forze-wiring/SKILL.md:20-30
Dependency Resolution Types
Dependencies are registered in the DepsRegistry in two distinct shapes:
- Plain Dependencies: Single providers per key, used for shared clients, secrets, or configuration defaults.
- Routed Dependencies: Providers keyed by both a
DepKeyand aroute(typicallyspec.name). This allows multiple document or search ports to coexist within the same application.
Sources: skills/forze-deps-consumption/SKILL.md:15-25
Lifecycle Orchestration
The LifecyclePlan manages the temporal aspects of the application, specifically the order in which connection pools are opened and closed.
Lifecycle Steps and Waves
Lifecycle management uses a topological sort to build execution "waves" based on dependencies between steps (requires, provides, depends_on).
- Startup: Steps are executed in order of their dependencies. Independent steps within a wave can be run in parallel using
with_concurrent(). - Shutdown: Steps are executed in reverse order. The system supports a
drain_timeout(default 10s) to allow for graceful resource release. - Fleet Deployment: For high-availability environments,
DeploymentProfile.FLEETcan be enabled to enforce safety checks on shared-state-mutating steps.
Sources: skills/forze-wiring/SKILL.md:85-105
Resource Bootstrapping Snippet
from forze_postgres import PostgresLifecycleModule
lifecycle_plan = LifecyclePlan.from_modules(
PostgresLifecycleModule(
client=pg_client,
dsn="postgresql://user:pass@localhost:5432/db",
config=PostgresConfig(min_size=2, max_size=15),
),
).with_steps(
redis_lifecycle_step(dsn="redis://localhost:6379/0"),
)
runtime = ExecutionRuntime(
deps=deps_registry.freeze(),
lifecycle=lifecycle_plan.freeze(),
)
Sources: skills/forze-wiring/SKILL.md:90-115
Interface Integration
The bootstrapping process culminates in attaching the execution runtime to an interface layer, most commonly FastAPI.
Lifespan Management
FastAPI's lifespan context manager is used to ensure the Forze ExecutionRuntime scope encompasses the entire duration of the web server's uptime.
Route Generation
Forze provides automated route generation from a frozen OperationRegistry. This process projects the logical operations (CRUD, search, storage) onto FastAPI APIRouter instances using either rest or rpc styles.
| Style | Description |
|---|---|
| REST | Resource-oriented paths (e.g., GET /notes/{id}, PATCH /notes/{id}). |
| RPC | Operation-named paths (e.g., GET /notes/get?id=, PATCH /notes/update?id=). |
Sources: skills/forze-fastapi-interface/SKILL.md:50-75
Summary of Bootstrapping Steps
The standard sequence for bootstrapping a Forze application follows these steps:
- Define Names: Use
StrEnumto define shared resource names. - Define Specs: Create logical
DocumentSpec,SearchSpec, etc. - Configure Infrastructure: Instantiate
DepsModulesmapping spec names to physical resources. - Compose Registries: Merge modules into
DepsRegistryandLifecyclePlan. - Bind Operations: Create an
OperationRegistry, bind transaction routes, and add middleware. - Freeze and Run: Freeze all registries, instantiate
ExecutionRuntime, and enter the runtime scope.
Sources: skills/forze-wiring/SKILL.md:15-30, skills/forze-specs-infrastructure/SKILL.md:15-35