Skip to main content

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}