Expand description
§System Lifecycle & Orchestration
This module manages the runtime lifecycle of actor-based systems, handling the complex coordination of starting, wiring, and shutting down multiple interdependent actors.
§The Orchestration Pattern
In actor systems, individual actors are simple, but wiring them together is where complexity lives. This module provides the “conductor” that coordinates the entire system.
Key Responsibilities:
- Actor Creation - Instantiate all actors and their clients
- Dependency Injection - Wire actors together via context injection
- Lifecycle Management - Start actors in the correct order
- Graceful Shutdown - Coordinate clean termination of all actors
- Observability Setup - Initialize tracing and logging infrastructure
§The OrderSystem Pattern
The OrderSystem demonstrates a complete lifecycle orchestrator:
impl OrderSystem {
pub fn new() -> Self {
// 1. Create actors (no dependencies yet - avoids circular refs)
let (user_actor, user_client) = user_actor::new();
let (product_actor, product_client) = product_actor::new();
let (order_actor, order_client) = order_actor::new();
// 2. Start actors with their dependencies injected
let user_handle = tokio::spawn(user_actor.run(()));
let product_handle = tokio::spawn(product_actor.run(()));
let order_handle = tokio::spawn(
order_actor.run((user_client.clone(), product_client.clone()))
);
Self {
user_client,
product_client,
order_client,
handles: vec![user_handle, product_handle, order_handle],
}
}
pub async fn shutdown(self) {
// Drop clients to signal shutdown, then await all actors
drop(self.user_client);
drop(self.product_client);
drop(self.order_client);
for handle in self.handles {
let _ = handle.await;
}
}
}§Dependency Injection via Context
The framework uses late binding to solve circular dependency problems:
- Construction time: Create actors without dependencies
- Runtime: Inject dependencies via
run(context)
This pattern allows Order to depend on User and Product without creating
circular references during construction.
Each actor defines its Context associated type:
// No dependencies
impl ActorEntity for User {
type Context = ();
}
// Depends on User and Product clients
impl ActorEntity for Order {
type Context = (UserClient, ProductClient);
}§Graceful Shutdown
The shutdown pattern follows these steps:
- Drop all clients - Closes the sender side of channels
- Actors detect closure -
receiver.recv()returnsNone - Actors clean up - Process remaining messages, log final state
- Await completion - Wait for all actor tasks to finish
This ensures no messages are lost and all actors terminate cleanly.
With Context Dependencies: When actors hold clients in their context (e.g., Order actor has UserClient and
ProductClient), those clients are clones and won’t prevent shutdown as long as the
dependency graph is acyclic. Each actor shuts down when its own channel closes.
For cyclic dependencies: Use an explicit Shutdown action instead of relying on
channel closure. This ensures deterministic shutdown order regardless of dependency structure.
§Observability & Tracing
The [setup_tracing] function initializes structured logging for the entire system.
The framework uses the tracing crate with hierarchical spans to trace:
- Actor lifecycle events (startup, shutdown)
- Entity operations (Create, Get, Update, Delete, Actions)
- Request flows with complete context
- Errors with detailed entity IDs
Usage:
RUST_LOG=info cargo run # Compact logs
RUST_LOG=debug cargo run # Full payloads📖 For complete tracing documentation, see the [tracing] module with detailed
examples, workflow traces, and best practices.
§Future Extensions
As systems grow, this module may include:
- Configuration management (loading from files/env)
- Health checks and readiness probes
- Metrics collection and export
- Actor registry for dynamic discovery
- Hot reload and zero-downtime updates
Re-exports§
pub use order_system::*;
Modules§
- order_
system - Order System