mcpField
M
m13-domain-error

m13-domain-error

github.com/actionbook开发1.6k3.3k 次安装9 周前更新
代码仓库

关于 m13-domain-error

面向 Rust 开发者的错误策略技能,帮助按受众、可恢复性和调试上下文设计错误类型,并选择相应恢复方式。

主要功能

  1. 01按受众和恢复方式分类错误
  2. 02区分瞬时与永久错误
  3. 03提供重试、回退和超时模式
  4. 04强调错误上下文与安全信息

适用场景

  1. 01设计应用错误类型
  2. 02规划服务故障的重试策略
  3. 03改善错误日志与用户提示

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/actionbook/rust-skills --skill m13-domain-error

安装后在 Claude Code 中输入/m13-domain-error确认已加载。

详细介绍

领域错误策略

第 2 层:设计选择

核心问题

谁需要处理此错误,以及应该如何恢复?

设计错误类型之前:

  • 这是面向用户的错误,还是内部错误?
  • 是否可以恢复?
  • 调试需要哪些上下文?

错误分类

错误类型面向对象恢复方式示例
面向用户终端用户指引操作InvalidEmail、NotFound
内部开发者调试信息DatabaseError、ParseError
系统运维/SRE监控/告警ConnectionTimeout、RateLimited
暂时性自动化流程重试NetworkError、ServiceUnavailable
永久性人员调查ConfigInvalid、DataCorrupted

思考提示

设计错误类型之前:

  1. 谁会看到此错误?

    • 终端用户 → 友好的消息,并能指导后续操作
    • 开发者 → 详细且便于调试的信息
    • 运维人员 → 结构化且可触发告警的信息
  2. 我们能否恢复?

    • 暂时性错误 → 退避后重试
    • 可降级错误 → 使用备用值
    • 永久性错误 → 快速失败并告警
  3. 需要哪些上下文?

    • 调用链 → anyhow::Context
    • 请求 ID → 结构化日志记录
    • 输入数据 → 错误负载

向上追溯 ↑

追溯到领域约束(第 3 层):

"How should I handle payment failures?"
    ↑ Ask: What are the business rules for retries?
    ↑ Check: domain-fintech (transaction requirements)
    ↑ Check: SLA (availability requirements)
问题追溯到询问
重试策略domain-*重试可以接受的延迟是多少?
用户体验domain-*应向用户显示什么消息?
合规性domain-*审计需要记录哪些内容?

向下追溯 ↓

追溯到实现(第 1 层):

"Need typed errors"
    ↓ m06-error-handling: thiserror for library
    ↓ m04-zero-cost: Error enum design

"Need error context"
    ↓ m06-error-handling: anyhow::Context
    ↓ Logging: tracing with fields

"Need retry logic"
    ↓ m07-concurrency: async retry patterns
    ↓ Crates: tokio-retry, backoff

快速参考

恢复模式适用场景实现方式
重试暂时性故障指数退避
备用方案降级模式缓存值/默认值
熔断器级联故障failsafe-rs
超时耗时操作tokio::time::timeout
舱壁隔离隔离独立线程池

错误层级

#[derive(thiserror::Error, Debug)]
pub enum AppError {
    // User-facing
    #[error("Invalid input: {0}")]
    Validation(String),

    // Transient (retryable)
    #[error("Service temporarily unavailable")]
    ServiceUnavailable(#[source] reqwest::Error),

    // Internal (log details, show generic)
    #[error("Internal error")]
    Internal(#[source] anyhow::Error),
}

impl AppError {
    pub fn is_retryable(&self) -> bool {
        matches!(self, Self::ServiceUnavailable(_))
    }
}

重试模式

use tokio_retry::{Retry, strategy::ExponentialBackoff};

async fn with_retry<F, T, E>(f: F) -> Result<T, E>
where
    F: Fn() -> impl Future<Output = Result<T, E>>,
    E: std::fmt::Debug,
{
    let strategy = ExponentialBackoff::from_millis(100)
        .max_delay(Duration::from_secs(10))
        .take(5);

    Retry::spawn(strategy, || f()).await
}

常见错误

错误做法错误原因更好的做法
所有错误都使用同一种类型无法采取针对性操作按面向对象分类
对所有错误都重试浪费资源仅重试暂时性错误
无限重试自我造成拒绝服务限制重试次数并采用退避策略
暴露内部错误安全风险使用对用户友好的消息
缺少上下文难以调试在所有地方使用 .context()

反模式

反模式问题更好的做法
字符串错误缺少结构使用 thiserror 类型
对可恢复错误使用 panic!用户体验差使用带上下文的 Result
忽略错误静默失败记录日志或向上传播
到处使用 Box丢失类型信息使用 thiserror
在正常流程中处理错误影响性能尽早验证

相关技能

适用场景参阅
错误处理基础m06-error-handling
重试实现m07-concurrency
领域建模m09-domain
面向用户的 APIdomain-*

评论

支持 Markdown