90个版本 (17个破坏性更新)

新版本 0.18.4 2024年8月21日
0.17.3 2024年8月13日
0.13.0 2024年7月31日
0.3.15 2024年3月21日
0.2.17 2023年11月19日

#605 in 异步

Download history 378/week @ 2024-05-01 232/week @ 2024-05-08 457/week @ 2024-05-15 190/week @ 2024-05-22 147/week @ 2024-05-29 513/week @ 2024-06-05 19/week @ 2024-06-12 408/week @ 2024-06-19 45/week @ 2024-06-26 336/week @ 2024-07-03 388/week @ 2024-07-10 477/week @ 2024-07-17 18/week @ 2024-07-24 547/week @ 2024-07-31 715/week @ 2024-08-07 350/week @ 2024-08-14

每月1,631次下载

MIT 许可证

70KB
1K SLoC

一个用于编写可靠和可扩展系统的基于事件的框架。

在较高层次上,它提供了一些主要组件

鲁瓦之旅

鲁瓦由多个模块组成,这些模块提供了一组对于在Rust中实现类似消息总线应用程序的必需功能。在本节中,我们将简要浏览,总结主要API及其用途。

TCommand & Event

您可以使用如下into_command派生宏将任何通用结构注册为以下内容

#[ruva::into_command]
pub struct MakeOrder {
    pub user_id: i64,
    pub items: Vec<String>,
}

当您附加into_command派生宏时,MessageBus现在将能够理解它应该如何以及在哪里调度命令。

同样,您可以对事件执行相同的操作

#[derive(Serialize, Deserialize, Clone, TEvent)]
#[internally_notifiable]
pub struct OrderFailed {
    pub user_id: i64,
}

#[derive(Serialize, Deserialize, Clone, TEvent)]
#[externally_notifiable(OrderAggregate)]
pub struct OrderSucceeded{
    #[identifier]
    pub id: i64,
    pub user_id: i64,
    pub items: Vec<String>
}

请注意,internally_notifiable事件不需要聚合规范,而externally_notifiable事件则需要,包括其id和identifier属性。

  • internally_notifiable是标记,让系统知道该事件应在应用程序内部处理
  • externally_notifiable事件作为OutBox存储。

初始化TCommandService

为了使消息总线能够识别服务处理器,必须实现TCommandService,其响应将直接发送给客户端。

pub struct MessageBus {
event_handler: &'static TEventHandler<ApplicationResponse, ApplicationError>,
}
impl ruva::TMessageBus<ApplicationResponse,ApplicationError, Command> for MessageBus{
fn command_handler(
    &self,
    context_manager: ruva::AtomicContextManager,
    cmd: Command,
) -> impl ruva::TCommandService<ApplicationResponse, ApplicationError> {
    HighestLevelOfAspectThatImplementTCommandService::new(
        MidLevelAspectThatImplementTCommandService::new(
            TargetServiceThatImplementTCommandService::new(
                cmd, other_dependency
            )
        )
    )
}
}

为了方便起见,鲁瓦提供了声明性宏,可以处理事务单元工作,您可以使用如下方式使用它

ruva::register_uow_services!(
	MessageBus,
	ServiceResponse,
	ServiceError,

	//Command => handler mapping
	CreateUserAccount => create_user_account,
	UpdatePassword => update_password,
    MakeOrder => make_order,
    DeliverProduct => deliver_product
)

注册事件

Event是命令处理或另一个事件处理的副作用。只要所有处理程序都消耗相同类型的Event,就可以注册尽可能多的处理程序,如下所示

示例

use ruva::ruva_core::init_event_handler;

init_event_handler!(
{
    Response,
    Error,
    |ctx| YourServiceEventHandler::new(ctx),

    OrderFaild: [
           NotificationHandler::send_mail,
           ],
           
    #[asynchronous]
    OrderSucceeded: [
           DeliveryHandler::checkout_delivery_items,
           InventoryHandler::change_inventory_count
           ]
}
);

MakeOrder TCommand处理中,我们会有OrderFailedOrderSucceeded事件,每个事件都有自己的处理程序。事件在处理程序中被触发,并由MessageBus抛出,由ContextManager触发。除非收到StopSentinel,否则MessageBus会循环处理所有处理程序。

处理程序API示例(文档所需)

MessageBus

核心是事件驱动的库是MessageBus,它从UnitOfWork获取命令和触发事件,并将事件分发给正确的处理程序。由于这仅在框架侧完成,因此您只有在调用它时才能“感觉到”messagebus的存在。其他所有事情都是神奇地完成的。

示例


#[ruva::into_command]
pub struct MakeOrder { // Test TCommand
    pub user_id: i64,
    pub items: Vec<String>
}

async fn test_func(){
    let bus = MessageBus::new(command_handler(), event_handler())
    let command = MakeOrder{user_id:1, items:vec!["shirts","jeans"]}
    match bus.execute_and_wait(command,Box::new(connection_pool())).await{
        Err(err)=> { // test for error case }
        Ok(val)=> { // test for happy case }
    }
    }
    }   
}

MessageBus的错误

当命令尚未注册时,它返回一个错误 - BaseError::NotFound请注意,与分布式事件处理不同,bus不返回事件处理的返回结果。

依赖关系

~8-25MB
~370K SLoC