ruva/
lib.rs

1//! [ruva-core]: https://docs.rs/ruva-core
2//! [ruva-macro]: https://docs.rs/ruva-macro
3//! [TCommand]: https://docs.rs/ruva-core/latest/ruva_core/message/trait.TCommand.html
4//! [TEvent]: https://docs.rs/ruva-core/latest/ruva_core/message/trait.TEvent.html
5//! [TMessageBus]: https://docs.rs/ruva-core/latest/ruva_core/messagebus/trait.TMessageBus.html
6//! [ContextManager]: https://docs.rs/ruva-core/latest/ruva_core/messagebus/struct.ContextManager.html
7//! [AtomicContextManager]: https://docs.rs/ruva-core/latest/ruva_core/messagebus/type.AtomicContextManager.html
8//! [TCommandService]: https://docs.rs/ruva-core/latest/ruva_core/handler/trait.TCommandService.html
9//! [TCommitHook]: https://docs.rs/ruva-core/latest/ruva_core/unit_of_work/trait.TCommitHook.html
10//! [into_command]: https://docs.rs/ruva-macro/latest/ruva_macro/attr.into_command.html
11//!
12//! A event-driven framework for writing reliable and scalable system.
13//!
14//! At a high level, it provides a few major components:
15//!
16//! * Tools for [core components with traits][ruva-core],
17//! * [Macros][ruva-macro] for processing events and commands
18//!
19//!
20//! # A Tour of Ruva
21//!
22//! Ruva consists of a number of modules that provide a range of functionality
23//! essential for implementing messagebus-like applications in Rust. In this
24//! section, we will take a brief tour, summarizing the major APIs and
25//! their uses.
26//!
27//! ## Command & Event
28//! You can register any general struct with [into_command] attribute macro as follows:
29//! ```rust,no_run
30//! #[ruva::into_command]
31//! pub struct MakeOrder {
32//!     pub user_id: i64,
33//!     pub items: Vec<String>,
34//! }
35//! ```
36//! Only when you attach [into_command] attribute macro, [TMessageBus] will be able to understand how and where it should
37//! dispatch the command to.
38//!
39//! To specify [TEvent] implementation, annotate struct with `TEvent` derive macro as in the following example:
40//! ```rust,ignore
41//! use serde::Serialize;
42//! use serde::Deserialize;
43//! use ruva::serde_json;
44//!
45//! #[derive(Serialize, Deserialize, Clone, ruva::TEvent)]
46//! #[internally_notifiable]
47//! pub struct OrderFailed {
48//!     pub user_id: i64,
49//! }
50//!
51//! #[derive(Serialize, Deserialize, Clone, ruva::TEvent)]
52//! #[externally_notifiable]
53//! pub struct OrderSucceeded{
54//!     #[identifier]
55//!     pub user_id: i64,
56//!     pub items: Vec<String>
57//! }
58//! ```
59//! Note that use of `internally_notifiable`(or `externally_notifiable`) and `identifier` are MUST.
60//!
61//! * `internally_notifiable` is marker to let the system know that the event should be handled
62//!    within the application
63//! * `externally_notifiable` is to leave `OutBox`.
64//! * `identifier` is to record aggregate id.
65//!
66//! This results in the following method attach to the struct for example,
67//! * `to_message()` : to convert the struct to heap allocated data structure so messagebus can handle them.
68//! * `state()` : to record event's state for outboxing
69//!
70//!
71//! ## Initializing TCommandService
72//! For messagebus to recognize service handler, [TCommandService] must be implemented, the response of which is sent directly to
73//! clients.
74//!
75//! ```rust,ignore
76//! pub struct MessageBus {
77//! event_handler: &'static TEventHandler<ApplicationResponse, ApplicationError>,
78//! }
79//!
80//! impl<C> ruva::TMessageBus<ApplicationResponse,ApplicationError,C> for MessageBus{
81//! fn command_handler(
82//!     &self,
83//!     context_manager: ruva::AtomicContextManager,
84//!     cmd: C,
85//! ) -> impl ruva::TCommandService<ApplicationResponse, ApplicationError> {
86//!         HighestLevelOfAspectThatImplementTCommandService::new(
87//!             MidLevelAspectThatImplementTCommandService::new(
88//!                 TargetServiceThatImplementTCommandService::new(cmd,other_depdendency)
89//!             )
90//!         )
91//! }
92//! }
93//! ```
94//! For your convenience, Ruva provides declarative macros that handles transaction unit of work as you can use it as follows:
95//!
96//! ```rust,ignore
97//! ruva::register_uow_services!(
98//!     MessageBus,
99//!     ServiceResponse,
100//!     ServiceError,
101//!
102//!     //Command => handler mapping
103//!     CreateUserAccount => create_user_account,
104//!     UpdatePassword => update_password,
105//!     MakeOrder => make_order,
106//!     DeliverProduct => deliver_product
107//! )
108//!
109//! ```
110//!
111//! ## Registering Event
112//!
113//! [TEvent] is a side effect of [TCommand] or yet another [TEvent] processing.
114//! You can register as many handlers as possible as long as they all consume same type of Event as follows:
115//!
116//! ### Example
117//!
118//! ```rust,ignore
119//! # macro_rules! init_event_handler {
120//! #    ($($tt:tt)*) => {}
121//! # }
122//! init_event_handler!(
123//! {
124//!    OrderFaild: [
125//!            NotificationHandler::send_mail,
126//!            ],
127//!    OrderSucceeded: [
128//!            DeliveryHandler::checkout_delivery_items,
129//!            InventoryHandler::change_inventory_count
130//!            ]
131//! }
132//! );
133//! ```
134//! In the `MakeOrder` TCommand Handling, we have either `OrderFailed` or `OrderSucceeded` event with their own processing handlers.
135//! Events are raised in the handlers that are thrown to [TMessageBus] by [ContextManager].
136//! [TMessageBus] then loops through the handlers UNLESS `StopSentinel` is received.
137//!
138//! ## Handler API Example
139//!
140//! Handlers can be located anywhere as long as they accept two argument:
141//! * msg - either [TCommand] or [TEvent]
142//! * context - [AtomicContextManager]
143//!
144//!
145//! ### Example
146//! ```rust,ignore
147//! use std::marker::PhantomData;
148//! use ruva_core::prelude::TUnitOfWork;
149//! use ruva_core::prelude::TSetCurrentEvents;
150//!
151//!
152//! // Service Handler
153//! pub struct CustomHandler<R> {
154//!     _r: PhantomData<R>,
155//! }
156//! impl<R> CustomHandler<R>
157//! where
158//!     R: TSetCurrentEvents + TUnitOfWork,
159//! {
160//!     pub async fn create_aggregate(
161//!         cmd: CreateCommand,
162//!         mut uow: R,
163//!     ) -> Result<ApplicationResponse, ApplicationError> {
164//!         // Transation begin
165//!         uow.begin().await?;
166//!         let mut aggregate: CustomAggregate = CustomAggregate::new(cmd);
167//!         uow.add(&mut aggregate).await?;
168//!
169//!         // Transation commit
170//!         uow.commit().await?;
171//!         Ok(aggregate.id.into())
172//!     }
173//! }
174//! ```
175//!
176//!
177//! ## Dependency Injection(For event handlers)
178//! For dependency to be injected into handlers, you just need to declare dependencies in `crate::dependencies` and
179//! specify identifiers for them. It's worth noting that at the moment, only parameterless function or function that takes
180//! [AtomicContextManager] are allowed.
181//!
182//! ### Example
183//!
184//! ```rust,ignore
185//! use ruva_core::init_event_handler;
186//! use ruva_core::prelude::TEvent;
187//! // crate::dependencies
188//! init_event_handler!(
189//!     ApplicationResponse,
190//!     ApplicationError,
191//!     |ctx| your_dependency(ctx),
192//!
193//!     SomethingHappened:[
194//!         handle_this_event_handler1,
195//!         handle_this_event_handler2,
196//!     ],
197//!     SomethingElseHappened:[
198//!         handle_this_event_handler3,
199//!         handle_this_event_handler4,
200//!     ],
201//! );
202//! ```
203//!
204//!
205//!
206//! ## TMessageBus
207//! At the core is event driven library is [TMessageBus], which gets command and take raised events from
208//! object that implements [TCommitHook] and dispatch the event to the right handlers.
209//! As this is done only in framework side, the only way you can 'feel' the presence of messagebus is
210//! when you invoke it. Everything else is done magically.
211//!
212//!
213//!
214//!
215//! #### Error from MessageBus
216//! When command has not yet been regitered, it returns an error - `BaseError::NotFound`
217//! Be mindful that bus does NOT return the result of event processing as in distributed event processing.
218
219pub extern crate static_assertions;
220
221pub use ruva_core::__register_uow_services_internal;
222pub use ruva_core::error;
223pub use ruva_core::init_event_handler;
224pub use ruva_core::make_conversion;
225pub use ruva_core::make_smart_pointer;
226pub use ruva_core::prelude::*;
227pub use ruva_core::prepare_bulk_operation;
228pub use ruva_core::register_uow_services;
229
230pub use ruva_macro::{aggregate, entity, event_hook, into_command, ApplicationError, ApplicationResponse, TConstruct, TEvent};