
## domain 目录深度讲解 domain 是项目的领域层Domain Layer遵循 **DDDDomain-Driven Design** 分层架构承载所有业务实体、数据访问对象、业务服务、外观整合与 Web 控制器是项目的核心业务逻辑层。 --- ### 目录结构总览与实际代码一致 src/domain/ ├── mod.rs # 入口:导出子模块 serde 序列化辅助(i64→字符串) ├── rudb/ # 核心数据库域 │ ├── dbentity/ # 领域实体(SeaORM 表结构映射) │ │ ├── plat_menu.rs / user_credits.rs SeaORM 自动生成实体 │ │ ├── t_sys_user.rs / sys_dept.rs 数据库表实体 │ │ ├── person.rs 手写示例实体(BaseEntity) │ │ ├── db_multi.rs 多实例模式示例 │ │ ├── *_init.rs 实体 Bean 自动注册(DI) │ │ └── *_test.rs #[cfg(test)] 单元测试 │ ├── dbdao/ # 数据访问对象(DAO) │ │ ├── plat_menu_dao.rs / t_sys_user_dao.rs DAO(基于 GeneralDb) │ │ ├── *_dao_init.rs DAO 自动注册(DI) │ │ └── *_dao_test.rs #[cfg(test)] │ ├── dbpage/ # 分页查询处理器 │ │ ├── user_credits_page.rs / t_sys_user_page.rs / plat_menu_page.rs │ │ ├── config_page.rs HTTP 配置展示页 │ │ ├── *_page_init.rs 分页 Bean 自动注册(DI) │ │ └── *_page_test.rs #[cfg(test)] │ ├── dbfacade/ # 外观整合层(Facade) │ │ ├── db_facade.rs 组合多个 DAO/实体 │ │ ├── db_facade_init.rs 外观 Bean 自动注册 │ │ └── db_facade_test.rs #[cfg(test)] │ └── dbweb/ # Web 控制器(Ctl) │ ├── user_credits_ctl.rs 对外暴露 REST 接口 │ └── mod.rs └── ruservice/ # 业务服务层(扩展) ├── ru_config_ext.rs run_config_ext_init.rs 配置扩展 ├── ru_cache_ext.rs ru_cache_ext_init.rs 缓存扩展 ├── web_client_ext.rs web_client_ext_init.rs Web 客户端扩展 └── 各 *_test.rs #[cfg(test)] --- ### 1️⃣ mod.rs — 入口与序列化辅助 [mod.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/mod.rs) 除了声明 rudb、ruservice 子模块并 pub use rudb::dbfacade::* 外定义了**两个关键的 serde 序列化辅助模块** - serde_i64_string将 i64 以**字符串形式**序列化/反序列化 - serde_option_i64_string同上但支持 Optioni64 **为什么** JS 端 Number 无法精确表示超过 2^53 的整数直接下发 i64 会丢失精度。转成字符串可保证数据库大 id 安全传向前端。 rust use serde::{self, Deserialize, Deserializer, Serializer}; pub mod serde_i64_string { pub fn serializeS(value: i64, serializer: S) - ResultS::Ok, S::Error where S: Serializer { serializer.serialize_str(value.to_string()) } pub fn deserializede, D(deserializer: D) - Resulti64, D::Error where D: Deserializerde { let s String::deserialize(deserializer)?; s.parse().map_err(serde::de::Error::custom) } } --- ### 2️⃣ rudb — 核心数据库域 [rudb/mod.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/mod.rs) 聚合 5 个子模块dbentity、dbdao、dbpage、dbfacade、dbweb。 #### ① dbentity — 领域实体 分两类 **A. SeaORM 自动生成实体**配 *_init.rs 自动注册 [plat_menu.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbentity/plat_menu.rs) rust #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)] #[sea_orm(table_name plat_menu)] #[serde(rename_all camelCase)] // 对外使用 camelCase pub struct Model { #[sea_orm(primary_key)] #[serde(with serde_i64_string)] // 大 id → 字符串 pub id: i64, pub menu_code: OptionString, pub menu_name: OptionString, pub created_at: OptionDateTimeWithTimeZone, ... } [user_credits.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbentity/user_credits.rs) 是另一个典型大表实体包含大量 Decimal/Option 字段和唯一键 idx_user_credits_user_currency_id。 **关键**一个 Model 既是 SeaORM 数据库映射又因 Serialize/Deserialize直接充当 API 的 DTO配 serde_i64_string 解决前端精度问题从而让实体即 DTO、无需额外转换层。 **B. 手写示例实体** [person.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbentity/person.rs)BaseEntity 多实例与 [db_multi.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbentity/db_multi.rs) rust // person.rs impl BaseEntity for Person {} // 多实例:每次 DI 获取新实例 impl GetSelf for Person {} // db_multi.rs impl BaseEntity for DbMulti {} // same impl DbMulti { pub fn execute(self) - String { ru_log::info2(RuMulti execute 123 {}, self.id); ... } } #### ② dbdao — 数据访问对象DAO [plat_menu_dao.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbdao/plat_menu_dao.rs) rust pub struct PlatMenuDao { #[serde(skip)] pub general_db: GeneralDbplat_menu::Entity, // 泛型数据库操作器 } impl BaseEntity for PlatMenuDao {} impl PlatMenuDao { pub fn new() - Self { Self { general_db: GeneralDb::plat_menu::Entity::new() } } } // ⭐ Deref 委托:DAO 自动获得 GeneralDb 的全部 CRUD 能力 impl Deref for PlatMenuDao { type Target GeneralDbplat_menu::Entity; fn deref(self) - Self::Target { self.general_db } } impl DerefMut for PlatMenuDao { ... } **Deref 委托模式**DAO 无需逐个转发 find_by_id / insert / update / delete / query_model 等方法通过 Deref/DerefMut 自动透明继承 GeneralDbT 的能力。 **DI 自动注入**[plat_menu_dao_init.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbdao/plat_menu_dao_init.rs) rust #[ctor(unsafe)] // 程序启动时自动执行,无需手动调用 pub fn init() { PlatMenuDao::register_bean(); } impl BeanSimple for PlatMenuDao { fn new_bean() - Self { let mut bean PlatMenuDao::new(); bean.init(); bean } } pub fn find_bean_plat_menu_dao() - OptionArcMutexPlatMenuDao { PlatMenuDao::find_bean() } // 线程安全 pub fn find_bean_plat_menu_dao_simple() - PlatMenuDao { PlatMenuDao::new_bean() } // 便捷获取 #### ③ dbpage — 分页查询处理器 [user_credits_page.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbpage/user_credits_page.rs) 通过 Deref 委托 SimplePage并提供业务化的分页查询编排 rust pub type SimplePageUserCredits SimplePageUserCreditsRequest, user_credits::Entity, user_credits::Model; pub struct UserCreditsPage { pub simple_page: SimplePageUserCredits } impl Deref for UserCreditsPage { ... target simple_page ... } impl UserCreditsPage { pub async fn list(mut self) - PageResult { self.build_query(); self.init_query(); // 业务查询条件(如 id0 时过滤) self.general_db.query_model().await } pub async fn ui_list(mut self) - PageResult { self.list().await } } [config_page.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbpage/config_page.rs) 是 BaseEntitySingle单例直接作为 HTTP Responder 暴露 /conf 配置接口展示领域层直接绑定 HTTP 路由的用法。 #### ④ dbfacade — 外观整合层 [db_facade.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbfacade/db_facade.rs) 组合多个组件对外提供统一入口 rust #[derive(Debug)] pub struct DbFacade { pub multi: DbMulti, // 业务实体 pub dao: PlatMenuDao, // DAO } impl BaseEntitySingle for DbFacade {} // 单例 impl DbFacade { pub fn new() - Self { Self { dao: find_bean_plat_menu_dao_simple(), // 从 DI 拉取 multi: find_bean_db_multi_simple(), } } pub fn execute(self) - String { ... } } [db_facade_init.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbfacade/db_facade_init.rs) 使用 register_bean_singleton() 注册**单例** rust #[ctor(unsafe)] pub fn init() { DbFacade::register_bean_singleton(); } impl BeanSingle for DbFacade { fn new_bean() - Self { let mut bean DbFacade::new(); bean.init(); bean } } **对比**BeanSimple/register_bean 多实例BeanSingle/register_bean_singleton 全局单例。DbFacade 是无状态整合对象故用单例。 #### ⑤ dbweb — Web 控制器 [user_credits_ctl.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/rudb/dbweb/user_credits_ctl.rs) 是领域层对外暴露的 REST 层覆盖标准 CRUD | 方法 | 路由 | 作用 | |------|------|------| | uilist | POST /api/v1/user_credits/uilist | 分页列表 | | uifind | GET /api/v1/user_credits/uifind/{id} | 按 id 查询 | | uidel | GET /api/v1/user_credits/uidel/{id} | 删除 | | uisave | POST /api/v1/user_credits/uisave | 新增(id0 → NotSet) | | uiupdate | POST /api/v1/user_credits/uiupdate | 更新 | rust pub async fn uilist(req: web::JsonSimpleRequestUserCreditsRequest) - impl Responder { let mut page find_bean_user_credits_page_simple(); // DI 获取分页处理器 page.set_uireq(req.into_inner()); let ret page.ui_list().await; res_ok(ret) // 统一响应包裹 } pub fn register() { reg_route!(api_v1_user_credits_uilist, /api/v1/user_credits/uilist, post, UserCreditsCtl::uilist); // ... 其余路由 } --- ### 3️⃣ ruservice — 业务服务层扩展 [ruservice/mod.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/ruservice/mod.rs) 提供三个扩展服务均以实现文件 独立 _init 自动注册组织。 - [ru_config_ext.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/ruservice/ru_config_ext.rs) RunConfigExt包装 RuConfig通过 Deref 透明委托提供 read_nats() 等按 DTO 读取配置的方法BaseEntitySingle 单例。 - [web_client_ext.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/ruservice/web_client_ext.rs) WebClientExt包装 WebClient 内置 RuCache超时 900sDeref 委托 WebClient。 - [ru_cache_ext.rs](file:///E:/soft/gitee.com/ruwebframe/src/domain/ruservice/ru_cache_ext.rs) RuCacheExt缓存扩展。 rust // ru_config_ext.rs —— Go 式组合/嵌入的 Rust 表达 #[derive(Debug, Clone, Deserialize, Serialize)] pub struct RunConfigExt { pub conf: RuConfig } impl BaseEntitySingle for RunConfigExt {} impl Deref for RunConfigExt { type Target RuConfig; fn deref(self) - RuConfig { self.conf } } impl DerefMut for RunConfigExt { ... } impl RunConfigExt { pub fn new() - Self { let mut c RunConfigExt { conf: RuConfig::new() }; c.init(); c } pub fn read_nats(self) - NatsDto { self.parse_object::NatsDto(nats) } } 每个扩展都有对应的 *_init.rs用 #[ctor(unsafe)] 在启动时注册 Bean业务代码通过 find_bean_*() 获取。 --- ### 数据流一次请求的完整旅程 浏览器 → POST /api/v1/user_credits/uilist [dbweb/user_credits_ctl.rs] → find_bean_user_credits_page_simple() [dbpage/ 经 DI 获取 Page] → page.ui_list() → list() [dbpage/ 业务编排 构建查询] → general_db.query_model() [dbdao/ 经 Deref 委托的 CRUD] → user_credits / plat_menu 实体 [dbentity/ SeaORM 映射] → PostgreSQL 数据库 ### 三大核心设计模式总结 | 模式 | 位置 | 作用 | |------|------|------| | **serde 辅助序列化** | mod.rs 的 serde_i64_string | i64 转字符串,解决前端精度丢失 | | **Deref/DerefMut 委托** | DAO、Page、各类 Ext | 组合内嵌对象并透明继承其能力,零胶水代码 | | **#[ctor] 自动注入(DI)** | 各 *_init.rs | 启动时自动注册 Bean,业务通过 find_bean_*() 获取 | 模块组织遵循统一的**四件套生成式骨架**xx.rs实现 xx_init.rsDI 注册 xx_test.rs#[cfg(test)] mod.rs导出从而让数据库表 → 实体 → DAO → Page → Ctl(HTTP)的全链路低代码化。