m13-domain-error

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Domain Error Strategy

领域错误策略

Layer 2: Design Choices
第二层:设计选择

Core Question

核心问题

Who needs to handle this error, and how should they recover?
Before designing error types:
  • Is this user-facing or internal?
  • Is recovery possible?
  • What context is needed for debugging?

谁需要处理这个错误,以及他们应该如何恢复?
在设计错误类型之前:
  • 这是面向用户的还是内部的?
  • 是否可以恢复?
  • 调试需要哪些上下文信息?

Error Categorization

错误分类

Error TypeAudienceRecoveryExample
User-facingEnd usersGuide action
InvalidEmail
,
NotFound
InternalDevelopersDebug info
DatabaseError
,
ParseError
SystemOps/SREMonitor/alert
ConnectionTimeout
,
RateLimited
TransientAutomationRetry
NetworkError
,
ServiceUnavailable
PermanentHumanInvestigate
ConfigInvalid
,
DataCorrupted

错误类型受众恢复方式示例
面向用户终端用户指导操作
InvalidEmail
,
NotFound
内部错误开发人员调试信息
DatabaseError
,
ParseError
系统错误运维/SRE监控/告警
ConnectionTimeout
,
RateLimited
临时错误自动化程序重试
NetworkError
,
ServiceUnavailable
永久错误人工调查处理
ConfigInvalid
,
DataCorrupted

Thinking Prompt

思考提示

Before designing error types:
  1. Who sees this error?
    • End user → friendly message, actionable
    • Developer → detailed, debuggable
    • Ops → structured, alertable
  2. Can we recover?
    • Transient → retry with backoff
    • Degradable → fallback value
    • Permanent → fail fast, alert
  3. What context is needed?
    • Call chain → anyhow::Context
    • Request ID → structured logging
    • Input data → error payload

在设计错误类型之前:
  1. 谁会看到这个错误?
    • 终端用户 → 友好提示,可执行操作
    • 开发人员 → 详细信息,便于调试
    • 运维人员 → 结构化,可告警
  2. 是否可以恢复?
    • 临时错误 → 带退避的重试
    • 可降级 → 回退值
    • 永久错误 → 快速失败,告警
  3. 需要哪些上下文信息?
    • 调用链 → anyhow::Context
    • 请求ID → 结构化日志
    • 输入数据 → 错误载荷

Trace Up ↑

向上追溯 ↑

To domain constraints (Layer 3):
"How should I handle payment failures?"
    ↑ Ask: What are the business rules for retries?
    ↑ Check: domain-fintech (transaction requirements)
    ↑ Check: SLA (availability requirements)
QuestionTrace ToAsk
Retry policydomain-*What's acceptable latency for retry?
User experiencedomain-*What message should users see?
Compliancedomain-*What must be logged for audit?

到领域约束(第三层):
"我应该如何处理支付失败?"
    ↑ 询问:重试的业务规则是什么?
    ↑ 查看:domain-fintech(交易要求)
    ↑ 查看:SLA(可用性要求)
问题追溯至询问内容
重试策略domain-*重试可接受的延迟是多少?
用户体验domain-*用户应该看到什么提示信息?
合规性domain-*审计需要记录哪些内容?

Trace Down ↓

向下追溯 ↓

To implementation (Layer 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

到实现层(第一层):
"需要类型化错误"
    ↓ m06-error-handling: 使用thiserror库
    ↓ m04-zero-cost: 错误枚举设计

"需要错误上下文"
    ↓ m06-error-handling: anyhow::Context
    ↓ 日志:带字段的tracing

"需要重试逻辑"
    ↓ m07-concurrency: 异步重试模式
    ↓ 依赖库:tokio-retry, backoff

Quick Reference

快速参考

Recovery PatternWhenImplementation
RetryTransient failuresexponential backoff
FallbackDegraded modecached/default value
Circuit BreakerCascading failuresfailsafe-rs
TimeoutSlow operations
tokio::time::timeout
BulkheadIsolationseparate thread pools
恢复模式适用场景实现方式
重试临时故障指数退避
回退降级模式缓存/默认值
熔断器级联故障failsafe-rs
超时慢操作
tokio::time::timeout
舱壁隔离独立线程池

Error Hierarchy

错误层级

rust
#[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(_))
    }
}
rust
#[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(_))
    }
}

Retry Pattern

重试模式

rust
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
}

rust
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
}

Common Mistakes

常见错误

MistakeWhy WrongBetter
Same error for allNo actionabilityCategorize by audience
Retry everythingWasted resourcesOnly transient errors
Infinite retryDoS selfMax attempts + backoff
Expose internal errorsSecurity riskUser-friendly messages
No contextHard to debug.context() everywhere

错误做法问题所在优化方案
所有场景使用相同错误无法执行针对性操作按受众分类
对所有错误重试浪费资源仅对临时错误重试
无限重试自我拒绝服务最大重试次数 + 退避
暴露内部错误安全风险面向用户的友好提示
无上下文信息难以调试处处使用.context()

Anti-Patterns

反模式

Anti-PatternWhy BadBetter
String errorsNo structurethiserror types
panic! for recoverableBad UXResult with context
Ignore errorsSilent failuresLog or propagate
Box<dyn Error> everywhereLost type infothiserror
Error in happy pathPerformanceEarly validation

反模式问题所在优化方案
字符串错误无结构thiserror类型
可恢复错误使用panic!用户体验差带上下文的Result
忽略错误静默故障记录或传播错误
处处使用Box<dyn Error>丢失类型信息thiserror
正常流程中处理错误性能问题提前验证

Related Skills

相关技能

WhenSee
Error handling basicsm06-error-handling
Retry implementationm07-concurrency
Domain modelingm09-domain
User-facing APIsdomain-*
适用场景参考内容
错误处理基础m06-error-handling
重试实现m07-concurrency
领域建模m09-domain
面向用户的APIdomain-*