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}