Contracts / Handlers / Domain 分层
基于 rust-webx 的业务应用采用**契约驱动、面向接口**的三层结构。关注契约,不关注实现。
contracts — API 与业务接口契约
**仅依赖框架**(`webx`),**禁止依赖 domain 或 handlers**。
// contracts/auth.rs — 定义「对外承诺什么」
use webx::*;
#[derive(Deserialize)]
pub struct LoginRequest {
pub email: String,
pub password: String,
}
#[derive(Serialize)]
pub struct AuthResponse {
pub token: String,
pub user: UserView,
}
#[derive(Serialize, Deserialize, Clone, Copy)]
pub enum UserRole {
Admin,
User,
}
/// 业务服务接口 — 与 Request 同级,同属契约层
pub trait IAuthService: Send + Sync {
fn login(&self, email: &str, password: &str) -> Result<AuthResponse, String>;
}
#[post("/api/auth/login")]
impl IRequest<AuthResponse> for LoginRequest {}
特点:
- Request、Response DTO、共享 enum、`I…Service` trait 均在此层 - 路由宏、`#[authorize]` 等元数据在此声明 - 是 OpenAPI 生成的数据源;团队讨论 API 时**只看 contracts** - **不含** `async fn` 业务实现、数据库访问
handlers — Handler 与 Service 实现
**履约层**:实现 `IRequestHandler` 与 `I…Service`。
// handlers/auth.rs — 定义「如何履约」
use crate::contracts::auth::{IAuthService, LoginRequest, AuthResponse};
use crate::domain::user::UserEntity;
#[inject]
pub struct AuthService {
ctx: Arc<Mutex<DbContext>>,
}
impl IAuthService for AuthService {
fn login(&self, email: &str, password: &str) -> Result<AuthResponse, String> {
// 读 UserEntity → 组装 AuthResponse(contracts DTO)
}
}
#[inject]
pub struct LoginHandler {
auth: Arc<dyn IAuthService>,
}
#[handler(inject)]
#[async_trait]
impl IRequestHandler<LoginRequest, AuthResponse> for LoginHandler {
async fn handle(&self, req: LoginRequest) -> Result<AuthResponse> {
self.auth.login(&req.email, &req.password)
.map_err(|e| Error::Validation(e))
}
}
特点:
- Handler 薄编排;复杂逻辑在 Service 实现中 - Handler **只注入** `Arc<dyn I…Service>` - `#[inject]` + `#[handler(inject)]` 自动注册,无需改 `main.rs` - 返回 `Result<T>`,不直接操作 HTTP
domain — 持久化实体与迁移
// domain/user.rs — 定义「数据是什么」
use crate::contracts::auth::UserRole; // 可复用 contracts 枚举
pub struct UserEntity {
pub id: String,
pub role: UserRole,
// ...
}
特点:
- 数据库实体、EF 配置、迁移 - **可以**引用 contracts 复用枚举或共享 model - **禁止**依赖框架类型(`serde` 除外) - **禁止**引用 handlers
数据流
HTTP Request
→ contracts (Request 反序列化)
→ handlers (Handler 编排)
→ Arc<dyn I…Service> (handlers 内实现)
→ domain (实体读写)
→ contracts (Response DTO 序列化)
→ HTTP Response
依赖规则速查
| 从 → 到 | contracts | handlers | domain | |---------|-----------|----------|--------| | contracts | — | ❌ | ❌ | | handlers | ✅ | — | ✅ | | domain | ✅(复用 enum/model) | ❌ | — |
反模式
- `contracts` 中 `use crate::domain::*` → DTO 应在 contracts,映射在 handlers - 独立 `services/` 目录 → 接口归 contracts,实现归 handlers - Handler 依赖 `Arc<AuthService>` → 应使用 `Arc<dyn IAuthService>`
小结
contracts 定义「承诺什么」,handlers 定义「如何履约」,domain 定义「数据是什么」。面向接口开发,让 API、实现与持久化各自独立演化。
下一节:[测试策略](testing-strategy.md)