event_driven_library/lib.rs
1//! [event-driven-core]: https://docs.rs/event-driven-core
2//! [event-driven-macro]: https://docs.rs/event-driven-macro
3//! [Command]: https://docs.rs/event-driven-core/latest/event_driven_core/message/trait.Command.html
4//! [Event]: https://docs.rs/event-driven-core/latest/event_driven_core/message/trait.Message.html
5//! [MessageBus]: https://docs.rs/event-driven-core/latest/event_driven_core/messagebus/index.html
6//! [Context]: https://docs.rs/event-driven-core/latest/event_driven_core/messagebus/struct.ContextManager.html
7//! [AtomicContextManager]: https://docs.rs/event-driven-core/latest/event_driven_core/messagebus/type.AtomicContextManager.html
8//!
9//! A event-driven framework for writing reliable and scalable system.
10//!
11//! At a high level, it provides a few major components:
12//!
13//! * Tools for [core components with traits][event-driven-core],
14//! * [Macros][event-driven-macro] for processing events and commands
15//!
16//!
17//! # A Tour of Event-Driven-Library
18//!
19//! Event-Driven-Library consists of a number of modules that provide a range of functionality
20//! essential for implementing messagebus-like applications in Rust. In this
21//! section, we will take a brief tour, summarizing the major APIs and
22//! their uses.
23//!
24//! ## Command & Event
25//! You can register any general struct with [Command] Derive Macro as follows:
26//! ```ignore
27//! #[derive(Command)]
28//! pub struct MakeOrder {
29//! pub user_id: i64,
30//! pub items: Vec<String>,
31//! }
32//! ```
33//! As you attach [Command] derive macro, MessageBus now is going to be able to understand how and where it should
34//! dispatch the command to.
35//!
36//! Likewise, you can do the same thing for Event:
37//! ```ignore
38//! #[derive(Serialize, Deserialize, Clone, Message)]
39//! #[internally_notifiable]
40//! pub struct OrderFailed {
41//! #[identifier]
42//! pub user_id: i64,
43//! }
44//!
45//! #[derive(Serialize, Deserialize, Clone, Message)]
46//! #[internally_notifiable]
47//! pub struct OrderSucceeded{
48//! #[identifier]
49//! pub user_id: i64,
50//! pub items: Vec<String>
51//! }
52//! ```
53//! Note that use of `internally_notifiable`(or `externally_notifiable`) and `identifier` is MUST.
54//!
55//! * `internally_notifiable` is marker to let the system know that the event should be handled
56//! within the application
57//! * `externally_notifiable` is to leave `OutBox`.
58//! * `identifier` is to record aggregate id.
59//!
60//!
61//!
62//! ## Initializing Command Handlers
63//! Command handlers are responsible for handling commands in an application, the response of which is sent directly to
64//! clients. Commands are imperative in nature, meaning they specify what should be done.
65//!
66//! ```
67//! # const IGNORE_1: &str = stringify! {
68//! use event_driven_library::prelude::{init_command_handler, init_event_handler};
69//! # };
70//! # macro_rules! init_command_handler {
71//! # ($($tt:tt)*) => {}
72//! # }
73//!
74//! init_command_handler!(
75//! {
76//! MakeOrder: OrderHandler::make_order,
77//! CancelOrder: OrderHandler::cancel_order
78//! }
79//! );
80//! ```
81//! In the example above, you see `MakeOrder` is mapped to `OrderHandler::make_order`, handler in application layer.
82//!
83//! At this point, imagine you want to handle both success/failure case of the `MakeOrder` command processing.
84//! Then you have to think about using event handlers.
85//!
86//! ## Registering Event
87//!
88//! `Event` is a side effect of [Command] or yet another [Event] processing.
89//! You can register as many handlers as possible as long as they all consume same type of Event as follows:
90//!
91//! ### Example
92//!
93//! ```
94//! # macro_rules! init_event_handler {
95//! # ($($tt:tt)*) => {}
96//! # }
97//! init_event_handler!(
98//! {
99//! OrderFaild: [
100//! NotificationHandler::send_mail,
101//! ],
102//! OrderSucceeded: [
103//! DeliveryHandler::checkout_delivery_items,
104//! InventoryHandler::change_inventory_count
105//! ]
106//! }
107//! );
108//! ```
109//! In the `MakeOrder` Command Handling, we have either `OrderFailed` or `OrderSucceeded` event with their own processing handlers.
110//! Events are raised in the handlers that are thrown to [MessageBus] by [Context].
111//! [MessageBus] then loops through the handlers UNLESS `StopSentinel` is received.
112//!
113//! ## Handler API Example
114//!
115//! Handlers can be located anywhere as long as they accept two argument:
116//!
117//! * msg - either [Command] or [Event]
118//! * context - [AtomicContextManager]
119//!
120//! ### Example
121//! ```ignore
122//! pub async fn make_order(
123//! cmd: MakeOrder,
124//! context: AtomicContextManager,
125//! ) -> Result<ServiceResponse, ServiceError> {
126//! let mut uow = UnitOfWork::<Repository<OrderAggregate>, TExecutor>::new(context).await;
127//!
128//! let mut order_aggregate = OrderAggregate::new(cmd);
129//! uow.repository().add(&mut task_aggregate).await?;
130//!
131//! uow.commit::<ServiceOutBox>().await?;
132//!
133//! Ok(().into())
134//! }
135//! ```
136//! But sometimes, you may want to add yet another dependencies. For that, Dependency Injection mechanism has been implemented.
137//! So, you can also do something along the lines of:
138//! ```ignore
139//! pub async fn make_order(
140//! cmd: MakeOrder,
141//! context: AtomicContextManager,
142//! payment_gateway_caller: Box<dyn Fn(String, Value) -> Future<(), ServiceError> + Send + Sync + 'static> //injected dependency
143//! ) -> Result<ServiceResponse, ServiceError> {
144//! let mut uow = UnitOfWork::<Repository<OrderAggregate>, TExecutor>::new(context).await;
145//!
146//! let mut order_aggregate = OrderAggregate::new(cmd,payment_gateway_caller);
147//! uow.repository().add(&mut task_aggregate).await?;
148//!
149//! uow.commit::<ServiceOutBox>().await?;
150//!
151//! Ok(().into())
152//! }
153//! ```
154//!
155//! How is this possible? because we preprocess handlers so it can allow for `DI container`.
156//!
157//! ## Dependency Injection
158//! You can simply register dependencies by putting attribute on top of free function.
159//!
160//! ### Example
161//!
162//! ```ignore
163//! // crate::dependencies
164//! pub fn payment_gateway_caller() -> Box<dyn Fn(String, Value) -> Future<(), ServiceError> + Send + Sync + 'static> {
165//! if cfg!(test) {
166//! __test_payment_gateway_caller() //Dependency For Test
167//! } else {
168//! __actual_payment_gateway_caller() //Real Dependency
169//! }
170//! }
171//! ```
172//!
173//! This is great as you can take your mind off static nature of the language.
174//!
175//!
176//!
177//! ## MessageBus
178//! At the core is event driven library is [MessageBus], which gets command and gets raised event from
179//! `UnitOfWork` and dispatch the event to the right handlers.
180//! As this is done only in framework side, the only way you can 'feel' the presence of messagebus is
181//! when you invoke it. Everything else is done magically.
182//!
183//!
184//!
185//! ### Example
186//! ```ignore
187//! #[derive(Command)]
188//! pub struct MakeOrder { // Test Command
189//! pub user_id: i64,
190//! pub items: Vec<String>
191//! }
192//!
193//! async fn test_func(){
194//! let bus = MessageBus::new(command_handler(), event_handler())
195//! let command = MakeOrder{user_id:1, items:vec!["shirts","jeans"]}
196//! match bus.handle(command).await{
197//! Err(err)=> { // test for error case }
198//! Ok(val)=> { // test for happy case }
199//! }
200//! }
201//! }
202//! }
203//! ```
204//!
205//! #### Error from MessageBus
206//! When command has not yet been regitered, it returns an error - `BaseError::NotFound`
207//! Be mindful that bus does NOT return the result of event processing as in distributed event processing.
208
209pub extern crate event_driven_core;
210pub extern crate event_driven_macro;
211pub extern crate static_assertions;
212
213pub mod prelude {
214 pub use event_driven_core::event_macros::*;
215 pub use event_driven_core::message::{Aggregate, Command, Message, MessageMetadata};
216 pub use event_driven_core::prelude::*;
217
218 pub use event_driven_macro::{entity, message_handler, Aggregate, ApplicationError, ApplicationResponse, Command, Message};
219}
220
221#[cfg(test)]
222mod application_error_derive_test {
223 use std::fmt::Display;
224
225 use crate as event_driven_library;
226 use event_driven_core::message::Message;
227 use event_driven_core::responses::{AnyError, BaseError};
228 use event_driven_macro::ApplicationError;
229
230 #[derive(Debug, ApplicationError)]
231 #[crates(event_driven_library)]
232 enum Err {
233 #[stop_sentinel]
234 Items,
235 #[stop_sentinel_with_event]
236 StopSentinelWithEvent(Box<dyn Message>),
237 #[database_error]
238 DatabaseError(Box<AnyError>),
239 BaseError(BaseError),
240 }
241
242 impl Display for Err {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 match self {
245 Self::Items => write!(f, "items"),
246 Self::StopSentinelWithEvent(item) => write!(f, "{:?}", item),
247 Self::DatabaseError(err) => write!(f, "{:?}", err),
248 Self::BaseError(err) => write!(f, "{:?}", err),
249 }
250 }
251 }
252}