actor_sample/lifecycle/order_system.rs
1//! # Order System
2//!
3//! Provides the runtime orchestrator for the actor‑based order management system.
4//! It wires together the `User`, `Product`, and `Order` actors and exposes
5//! high‑level clients for interacting with them. Includes lifecycle management
6//! and graceful shutdown.
7use crate::clients::{OrderClient, ProductClient, UserClient};
8use tracing::{error, info};
9
10/// The main runtime orchestrator for the actor-based order management system.
11///
12/// `OrderSystem` is responsible for:
13/// - **Lifecycle Management**: Starting and stopping all actors in the system
14/// - **Dependency Wiring**: Connecting actors that depend on each other (e.g., OrderClient needs UserClient)
15/// - **Resource Coordination**: Managing shared resources like ID generators
16///
17/// # Architecture
18///
19/// The system consists of three main actors:
20/// - **User Actor**: Manages user entities (CRUD operations)
21/// - **Product Actor**: Manages product entities with stock tracking
22/// - **Order Actor**: Manages orders and coordinates with User and Product actors
23///
24/// # Example
25///
26/// ```ignore
27/// let system = OrderSystem::new();
28///
29/// // Use the clients to interact with actors
30/// let user_id = system.user_client.create_user(user_data).await?;
31/// let product_id = system.product_client.create_product(product_data).await?;
32/// let order_id = system.order_client.create_order(order_data).await?;
33///
34/// // Gracefully shut down when done
35/// system.shutdown().await?;
36/// ```
37pub struct OrderSystem {
38 /// Client for interacting with the Order actor
39 pub order_client: OrderClient,
40
41 /// Client for interacting with the User actor
42 pub user_client: UserClient,
43
44 /// Client for interacting with the Product actor
45 pub product_client: ProductClient,
46
47 /// Task handles for all running actors (used for graceful shutdown)
48 handles: Vec<tokio::task::JoinHandle<()>>,
49}
50
51impl Default for OrderSystem {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl OrderSystem {
58 /// Creates and initializes a new `OrderSystem` with all actors running.
59 ///
60 /// This method:
61 /// 1. Creates ID generators for each entity type
62 /// 2. Spawns ResourceActors for User, Product, and Order
63 /// 3. Wires up dependencies (OrderClient depends on UserClient and ProductClient)
64 /// 4. Spawns each actor in its own Tokio task
65 ///
66 /// # Returns
67 ///
68 /// A fully initialized `OrderSystem` with all actors running and ready to accept requests.
69 pub fn new() -> Self {
70 // 1. Create actors (no dependencies) and wrap generic clients
71 let (user_actor, user_generic_client) = crate::user_actor::new();
72 let user_client = UserClient::new(user_generic_client);
73 let (product_actor, product_generic_client) = crate::product_actor::new();
74 let product_client = ProductClient::new(product_generic_client);
75 let (order_actor, order_generic_client) = crate::order_actor::new();
76 let order_client = OrderClient::new(order_generic_client);
77
78 // 2. Start actors with injected context
79 // User and Product have no dependencies (Context = ())
80 let user_handle = tokio::spawn(user_actor.run(()));
81 let product_handle = tokio::spawn(product_actor.run(()));
82
83 // Order actor needs User and Product clients (Context = (UserClient, ProductClient))
84 let order_handle =
85 tokio::spawn(order_actor.run((user_client.clone(), product_client.clone())));
86
87 Self {
88 order_client,
89 user_client,
90 product_client,
91 handles: vec![user_handle, product_handle, order_handle],
92 }
93 }
94
95 /// Gracefully shuts down the entire system.
96 ///
97 /// This method:
98 /// 1. Drops all clients, which closes their communication channels
99 /// 2. Waits for all actor tasks to complete
100 /// 3. Returns an error if any actor task panicked
101 ///
102 /// # Shutdown Process
103 ///
104 /// When clients are dropped, the underlying channels are closed. Each `ResourceActor`
105 /// detects the closed channel and exits its event loop gracefully.
106 ///
107 /// # Returns
108 ///
109 /// - `Ok(())` if all actors shut down cleanly
110 /// - `Err(String)` if any actor task failed or panicked
111 ///
112 /// # Example
113 ///
114 /// ```ignore
115 /// let system = OrderSystem::new();
116 /// // ... use the system ...
117 /// system.shutdown().await?;
118 /// ```
119 pub async fn shutdown(self) -> Result<(), String> {
120 info!("Shutting down system...");
121
122 // =====================================================================
123 // Step 1: Close all channels by dropping clients
124 // =====================================================================
125
126 // When we drop the clients, their internal channel senders are dropped.
127 // This causes the actors' receivers to return None, signaling shutdown.
128 drop(self.order_client);
129 drop(self.user_client);
130 drop(self.product_client);
131
132 // =====================================================================
133 // Step 2: Wait for all actor tasks to complete
134 // =====================================================================
135
136 for handle in self.handles {
137 // Wait for the actor task to finish
138 // If the task panicked, this will return an Err
139 if let Err(e) = handle.await {
140 error!("Actor task failed: {:?}", e);
141 return Err(format!("Actor task failed: {:?}", e));
142 }
143 }
144
145 info!("System shutdown complete.");
146 Ok(())
147 }
148}