discern/query.rs
1//! The `query` module defines the core abstractions and components related to handling queries in the CQRS pattern.
2//!
3//! Queries are used to retrieve data without modifying the state of the system. This module provides the `Query` trait,
4//! which represents a query, and the `QueryHandler` trait, which defines the behavior for handling queries.
5//!
6//! The `QueryBus` is responsible for dispatching queries to their respective handlers. It utilizes the
7//! `QueryHandlerRegistry` from the [registry](crate::registry) module to manage and retrieve the appropriate handlers.
8//!
9//! - [Query]: Represents a query in the system.
10//! - [QueryHandler]: Trait for handling queries.
11//! - [QueryBus]: Dispatches queries to the appropriate handlers.
12//!
13//! # See Also
14//!
15//! - [QueryHandlerRegistry]: Manages query handlers.
16
17use std::any::Any;
18use std::fmt::Debug;
19use std::sync::Arc;
20
21use crate::async_trait;
22use crate::registry::QueryHandlerRegistry;
23
24/// The `Query` trait represents a query that retrieves data from the system.
25///
26/// Queries are typically used to retrieve information without modifying the state of the system.
27///
28/// # Example
29///
30/// ```
31/// use discern::query::Query;
32///
33/// #[derive(Debug)]
34/// struct User {
35/// id: u64,
36/// username: String,
37/// email: String,
38/// }
39///
40/// #[derive(Debug)]
41/// enum GetUserError {
42/// UserNotFound,
43/// DatabaseError,
44/// }
45///
46/// #[derive(Debug)]
47/// struct GetUserQuery {
48/// user_id: u64,
49/// }
50///
51/// impl Query for GetUserQuery {
52/// // The data retrieved by the query, in this case, a `User`.
53/// type Output = User;
54/// // The error type that is returned if the query fails.
55/// type Error = GetUserError;
56/// }
57/// ```
58pub trait Query: Send + Sync + Any + Debug {
59 /// The output type that represents the data retrieved by the query.
60 ///
61 /// This type must implement the `Send` and `Sync` traits.
62 type Output: Send + Sync;
63
64 /// The error type that is returned if the query fails.
65 ///
66 /// This type must implement the `Debug`, `Send`, and `Sync` traits.
67 type Error: Debug + Send + Sync;
68}
69
70/// The `QueryHandler` trait represents a handler that processes a query.
71///
72/// # Example
73///
74/// ```
75/// # use discern::query::Query;
76/// # use discern::async_trait;
77/// #
78/// # #[derive(Debug)]
79/// # struct User {
80/// # id: u64,
81/// # username: String,
82/// # email: String,
83/// # }
84/// #
85/// # #[derive(Debug)]
86/// # enum GetUserError {
87/// # UserNotFound,
88/// # DatabaseError,
89/// # }
90/// #
91/// # #[derive(Debug)]
92/// # struct GetUserQuery {
93/// # user_id: u64,
94/// # }
95/// #
96/// # impl Query for GetUserQuery {
97/// # type Output = User;
98/// # type Error = GetUserError;
99/// # }
100/// #
101/// use discern::query::QueryHandler;
102///
103/// struct GetUserQueryHandler;
104///
105/// #[async_trait]
106/// impl QueryHandler<GetUserQuery> for GetUserQueryHandler {
107/// async fn handle(&self, query: GetUserQuery) -> Result<User, GetUserError> {
108/// // retrieve the user from the persistence layer...
109/// # Ok(User {
110/// # id: query.user_id,
111/// # username: "Alice".to_string(),
112/// # email: "alice@example.com".to_string(),
113/// # })
114/// }
115/// }
116/// ```
117#[async_trait]
118pub trait QueryHandler<Q: Query>: Send + Sync {
119 /// Handles the processing of a query.
120 ///
121 /// # Arguments
122 ///
123 /// * `query` - The query to be processed.
124 ///
125 /// # Returns
126 ///
127 /// The result of the query handler, which includes the output data or an error.
128 async fn handle(&self, query: Q) -> Result<Q::Output, Q::Error>;
129}
130
131/// The `QueryBus` is responsible for dispatching queries to their respective handlers.
132///
133/// Queries are dispatched through the `QueryBus` to the appropriate handler, which processes
134/// the query and returns the result.
135///
136/// # Example
137///
138/// ```
139/// # let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
140/// # rt.block_on(async {
141/// # use discern::query::Query;
142/// # use discern::async_trait;
143/// # use discern::query::QueryHandler;
144/// #
145/// # #[derive(Debug)]
146/// # struct User {
147/// # id: u64,
148/// # username: String,
149/// # email: String,
150/// # }
151/// #
152/// # #[derive(Debug)]
153/// # enum GetUserError {
154/// # UserNotFound,
155/// # DatabaseError,
156/// # }
157/// #
158/// # #[derive(Debug)]
159/// # struct GetUserQuery {
160/// # user_id: u64,
161/// # }
162/// #
163/// # impl Query for GetUserQuery {
164/// # type Output = User;
165/// # type Error = GetUserError;
166/// # }
167/// #
168/// # struct GetUserQueryHandler;
169/// #
170/// # #[async_trait]
171/// # impl QueryHandler<GetUserQuery> for GetUserQueryHandler {
172/// # async fn handle(&self, query: GetUserQuery) -> Result<User, GetUserError> {
173/// # // Retrieve user data.
174/// # Ok(User {
175/// # id: query.user_id,
176/// # username: "Alice".to_string(),
177/// # email: "alice@example.com".to_string(),
178/// # })
179/// # }
180/// # }
181/// use discern::query_registry;
182/// use discern::query::QueryBus;
183///
184/// let registry = query_registry! {
185/// GetUserQuery => GetUserQueryHandler { /* ... */ },
186/// };
187///
188/// let query_bus = QueryBus::new(registry);
189///
190/// let query = GetUserQuery {
191/// user_id: 1,
192/// };
193///
194/// let result = query_bus.dispatch(query).await;
195/// match result {
196/// Ok(user) => {
197/// # assert_eq!(user.username, "Alice".to_string());
198/// println!("User name: {}", user.username);
199/// },
200/// Err(err) => {
201/// # assert!(false);
202/// match err {
203/// GetUserError::UserNotFound => println!("User not found"),
204/// GetUserError::DatabaseError => println!("A database error occurred"),
205/// }
206/// }
207/// }
208/// # });
209/// ```
210#[derive(Clone, Debug)]
211pub struct QueryBus {
212 #[doc(hidden)]
213 registry: Arc<QueryHandlerRegistry>,
214}
215
216/// The `QueryBus` implementation.
217impl QueryBus {
218 /// Creates a new `QueryBus` instance.
219 ///
220 /// The `QueryBus` is initialized with a `QueryHandlerRegistry`, which contains
221 /// all the registered query handlers.
222 ///
223 /// # Arguments
224 ///
225 /// * `registry` - The query handler registry.
226 ///
227 /// # Example
228 ///
229 /// ```
230 /// use discern::query::QueryBus;
231 /// use discern::registry::QueryHandlerRegistry;
232 ///
233 /// let registry = QueryHandlerRegistry::new();
234 /// let query_bus = QueryBus::new(registry);
235 ///
236 /// # assert!(true);
237 /// ```
238 pub fn new(registry: QueryHandlerRegistry) -> Self {
239 Self {
240 registry: Arc::new(registry),
241 }
242 }
243
244 /// Dispatches a query to its respective handler.
245 ///
246 /// # Arguments
247 ///
248 /// * `query` - The query to dispatch.
249 ///
250 /// # Returns
251 ///
252 /// The result of the query handler, which includes the output data or an error.
253 ///
254 /// # Panics
255 ///
256 /// This method will panic if the query handler is not found.
257 ///
258 /// # Example
259 ///
260 /// ```
261 /// # let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
262 /// # rt.block_on(async {
263 /// # use discern::query::Query;
264 /// # use discern::async_trait;
265 /// # use discern::query::QueryHandler;
266 /// #
267 /// # #[derive(Debug)]
268 /// # struct User {
269 /// # id: u64,
270 /// # username: String,
271 /// # email: String,
272 /// # }
273 /// #
274 /// # #[derive(Debug)]
275 /// # enum GetUserError {
276 /// # UserNotFound,
277 /// # DatabaseError,
278 /// # }
279 /// #
280 /// # #[derive(Debug)]
281 /// # struct GetUserQuery {
282 /// # user_id: u64,
283 /// # }
284 /// #
285 /// # impl Query for GetUserQuery {
286 /// # type Output = User;
287 /// # type Error = GetUserError;
288 /// # }
289 /// #
290 /// # struct GetUserQueryHandler;
291 /// #
292 /// # #[async_trait]
293 /// # impl QueryHandler<GetUserQuery> for GetUserQueryHandler {
294 /// # async fn handle(&self, query: GetUserQuery) -> Result<User, GetUserError> {
295 /// # Ok(User {
296 /// # id: query.user_id,
297 /// # username: "Alice".to_string(),
298 /// # email: "alice@example.com".to_string(),
299 /// # })
300 /// # }
301 /// # }
302 /// use discern::query::QueryBus;
303 /// use discern::query_registry;
304 ///
305 /// let registry = query_registry! {
306 /// GetUserQuery => GetUserQueryHandler { /* ... */ },
307 /// };
308 ///
309 /// let query_bus = QueryBus::new(registry);
310 ///
311 /// let result = query_bus.dispatch(GetUserQuery { user_id: 1 }).await;
312 ///
313 /// match result {
314 /// Ok(user) => {
315 /// # assert_eq!(user.username, "Alice".to_string());
316 /// println!("User name: {}", user.username);
317 /// },
318 /// Err(err) => {
319 /// # assert!(false);
320 /// match err {
321 /// GetUserError::UserNotFound => println!("User not found"),
322 /// GetUserError::DatabaseError => println!("A database error occurred"),
323 /// }
324 /// }
325 /// }
326 /// # });
327 /// ```
328 pub async fn dispatch<Q: Query>(&self, query: Q) -> Result<Q::Output, Q::Error> {
329 match self.registry.get_handler::<Q>() {
330 Some(handler) => handler.handle(query).await,
331 None => {
332 panic!(
333 "No handler registered for query: {:?}",
334 std::any::type_name::<Q>()
335 );
336 }
337 }
338 }
339}