lrwf_core/handler.rs
1//! Handler traits: IRequestHandler and IEventHandler.
2//!
3//! ## IRequestHandler<T, R>
4//!
5//! Dual-type-parameter handler: `T` is the request type, `R` is the response type.
6//! The constraint `T: IRequest<R>` ensures type safety between request and response.
7//!
8//! ```ignore
9//! #[async_trait]
10//! impl IRequestHandler<GetUserRequest, UserModel> for GetUserHandler {
11//! async fn handle(&self, req: GetUserRequest) -> Result<UserModel> { ... }
12//! }
13//! ```
14
15use crate::auth::IClaims;
16use crate::error::Result;
17use crate::mediator::{IEventRequest, IRequest};
18
19/// Handles a single `IRequest<R>`, producing its associated response `R`.
20///
21/// Register via `#[handler]` proc macro for compile-time collection,
22/// or use `register_handlers!` for manual DI registration.
23///
24/// ```ignore
25/// #[async_trait]
26/// impl IRequestHandler<GetUserRequest, UserModel> for GetUserHandler {
27/// async fn handle(&self, req: GetUserRequest) -> Result<UserModel> { ... }
28/// }
29/// ```
30#[async_trait::async_trait]
31pub trait IRequestHandler<T, R>: Send + Sync
32where
33 T: IRequest<R> + Send + 'static,
34 R: serde::Serialize + Send + 'static,
35{
36 /// Handle the request without claims.
37 async fn handle(&self, req: T) -> Result<R>;
38
39 /// Handle the request with optional authentication claims.
40 async fn handle_with_claims(&self, req: T, claims: Option<&dyn IClaims>) -> Result<R> {
41 let _ = claims;
42 self.handle(req).await
43 }
44}
45
46/// Handles a single `IEventRequest`, performing side effects.
47///
48/// ```ignore
49/// #[async_trait]
50/// impl IEventHandler<UserCreatedEvent> for SendWelcomeEmailHandler {
51/// async fn handle(&self, event: UserCreatedEvent) -> Result<()> { ... }
52/// }
53/// ```
54#[async_trait::async_trait]
55pub trait IEventHandler<T: IEventRequest>: Send + Sync {
56 async fn handle(&self, event: T) -> Result<()>;
57}
58
59/// Background service that is started when the host starts and
60/// stopped when the host performs a graceful shutdown.
61///
62/// Analogous to ASP.NET Core's IHostedService.
63///
64/// Use this for:
65/// - Data initialization / seeding at application startup
66/// - Background polling loops
67/// - Queue consumers
68/// - Connection pool warmup
69///
70/// # Example
71///
72/// ```ignore
73/// #[derive(Default)]
74/// struct DbInitService;
75///
76/// #[async_trait]
77/// impl IHostedService for DbInitService {
78/// async fn start(&self) -> Result<()> {
79/// tracing::info!("[DbInitService] Running migrations...");
80/// run_migrations().await?;
81/// tracing::info!("[DbInitService] Seeding data...");
82/// seed_data().await?;
83/// Ok(())
84/// }
85///
86/// async fn stop(&self) -> Result<()> {
87/// tracing::info!("[DbInitService] Shutting down...");
88/// Ok(())
89/// }
90/// }
91/// ```
92#[async_trait::async_trait]
93pub trait IHostedService: Send + Sync {
94 /// Called when the host starts.
95 ///
96 /// The host waits for all hosted services to finish `start()`
97 /// before beginning to accept incoming requests.
98 async fn start(&self) -> Result<()>;
99
100 /// Called during a graceful shutdown.
101 ///
102 /// The host calls `stop()` on all hosted services concurrently
103 /// after the HTTP server has stopped accepting new connections.
104 async fn stop(&self) -> Result<()> {
105 Ok(())
106 }
107}