build-backend-service-patterns

star 15

服务架构模式——分层、通信、韧性。当需要设计后端服务架构、跨服务通信或处理分布式问题,或提到"微服务""重试""熔断"

ZeroZ-lab By ZeroZ-lab schedule Updated 5/16/2026

name: build-backend-service-patterns description: 服务架构模式——分层、通信、韧性。当需要设计后端服务架构、跨服务通信或处理分布式问题,或提到"微服务""重试""熔断"

Service Patterns — 服务架构模式

入口/出口

  • 入口: 涉及多个服务/模块的边界设计、通信模式选择、韧性策略
  • 出口: 模式选择记录 + 接口定义 + 韧性配置
  • 指向: 架构确定后进入 build-backend-api-design(定义具体接口)
  • 输出路径: verify-workflow-review(下游验证技能)
  • 前置加载: CANON.md

何时不使用

  • 只是在单服务内部实现一个清晰函数或端点
  • 服务边界、通信方式和容错策略已经由 spec/ADR 明确
  • 问题集中在具体 API 合约或数据库 schema,而不是服务架构

服务分层

Presentation  →  API Handler / Controller(HTTP、验证、序列化)
     │
Business      →  Service(领域逻辑、规则、工作流)
     │
Data          →  Repository / Adapter(数据访问、外部 API 封装)
     │
Infrastructure →  DB、Cache、Queue、外部服务

依赖方向: 上层依赖下层,依层不跨层。Business 不直接 import HTTP 框架类型。

通信模式

模式 适用场景 示例
同步 Request-Response 需要立即结果、强一致性 创建订单 → 返回订单号
异步 Message Queue 可延迟、高吞吐 订单创建后发送邮件通知
Event-Driven Pub/Sub 多消费者、解耦 订单完成 → (通知服务 + 分析服务 + 积分服务)
Saga (编排) 跨服务长事务 下单 → 扣库存 → 扣款 → 通知(任一步失败 = 补偿回滚)

选择原则:

需要立即响应 + 强一致?→ 同步
可延迟 + 需要韧性?  → 异步
多消费者 + 解耦?   → Event-Driven
跨服务长事务?      → Saga

韧性模式

重试与退避

async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (attempt === maxRetries) throw err;
      const delay = Math.pow(2, attempt) * 100;  // 100ms → 200ms → 400ms
      await sleep(delay);
    }
  }
  throw new Error('unreachable');
}

断路器

状态机:
  CLOSED → (连续失败 N 次) → OPEN(立即失败,不调用)
  OPEN → (等待 timeout) → HALF_OPEN(尝试一次)
  HALF_OPEN → 成功 → CLOSED | 失败 → OPEN

断路器保护下游服务不被雪崩淹没。 当调用已经反复失败时,快速失败 > 继续重试拖死上游。

幂等性

// 每个写操作需要幂等键
interface CreateOrderRequest {
  idempotencyKey: string;  // 客户端生成 UUID
  items: OrderItem[];
}

// 服务端: 相同幂等键 → 返回相同结果(不重复创建)
async function createOrder(req: CreateOrderRequest): Promise<Order> {
  const existing = await db.orders.findBy({ idempotencyKey: req.idempotencyKey });
  if (existing) return existing;
  return db.orders.create(req);
}

任何涉及支付的端点必须有幂等键。 网络重试 + 重复扣款 = 灾难。

模式选择

需求 模式 复杂度代价
CRUD 围绕单一实体 Repository + Service
读/写负载不对称 CQRS(读写分离)
跨服务事务 Saga
复杂业务规则链 Chain of Responsibility / Pipeline
多策略可切换 Strategy Pattern
领域逻辑密集 Domain-Driven(Entity + Value Object + Aggregate) 中-高

原则: 从最简单的模式开始。当代码变复杂因为模式不够时才升级,不因为"如此"。

错误处理

// 区分可恢复错误 vs 不可恢复错误
class RetryableError extends Error { /* 网络超时、503 */ }
class FatalError extends Error { /* 验证失败、404 */ }

async function handleServiceCall(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (err) {
    if (err instanceof RetryableError) {
      return withRetry(fn);
    }
    throw err;  // Fatal — 上层处理
  }
}

常见说辞

说辞 现实 后果
"以后需要时再加重试" 网络是不确定的。从第一条跨服务调用开始就加重试。 无重试 → 单次网络抖动导致请求失败率从 0.1% 飙升到 5-10%,生产故障排查时间 2-8 小时
"断路器太复杂,直接重试" 一直重试失败的服务 = 拖着上游一起死。断路器是保护装置。 无断路器 → 下游宕机时上游持续重试耗尽线程池,级联故障扩散到 3-5 个服务
"幂等性以后补" 有支付的系统从第一天就需要。补幂等性需要改 API 合约。 缺幂等键 → 网络重试导致重复扣款/重复创建,单次事故赔偿成本 > 开发成本 10 倍
"同步耦合没关系,微服务间调用很快" 现在快。调用链 lengthens,故障面扩大。能不耦合就不耦合。 同步链 > 3 层 → 平均故障恢复时间翻倍,任何一层超时导致整条链失败
"选 CQRS,虽然简单但万一以后要读扩展" 为"万一"增加复杂度 = 过早工程化。简单需求简单方案。 过早 CQRS → 读写模型同步延迟 bug、运维复杂度提升 3-5 倍,90% 项目不需要

验证失败处理

失败场景 处理方式
跨服务 HTTP 调用无重试和超时 停止,为每个跨服务调用添加 withRetry + timeout 配置后再继续
支付相关端点缺少幂等键 停止,添加 idempotencyKey 字段到 API 合约,服务端实现幂等检查后再继续
队列消费者无死信队列 添加 DLQ 配置,失败消息进入 DLQ 而非反复消费,避免队列堵塞
同步调用链 > 3 层 重构为异步解耦或引入 Saga 编排,不允许串行穿透超过 3 个服务
服务边界按技术层而非业务领域划分 重画服务边界图,按业务领域(如订单、支付、用户)重新划分

好坏示例

✅ Good: 最简模式 + 韧性完整

订单创建流程:
- API Handler → 调用 OrderService.createOrder()
- OrderService → 写入 DB + 发事件到 Queue(异步解耦)
- 邮件服务消费 Queue 事件(异步,不阻塞订单创建)

韧性配置:
- OrderService → DB: withRetry(3, exponentialBackoff)
- OrderService → Queue: 断路器(5 次连续失败打开)
- createOrder API: idempotencyKey 必填
- Queue 消费者: 死信队列(失败 3 次进入 DLQ)

模式选择: Repository + Service(最简,匹配 CRUD 围绕单一实体需求)

❌ Bad: 过早工程化 + 缺韧性

订单创建流程:
- API → OrderService → InventoryService → PaymentService → NotificationService
  (同步链 4 层,任何一层超时整条链失败)

韧性配置: (未配置重试、断路器、幂等键)

模式选择: CQRS + Saga + Event Sourcing
  (当前只需 CRUD,过早引入 3 个高复杂度模式)

红旗 — STOP

  • 单体拆成微服务但没有定义故障模式(网络分区、超时、下游宕机)
  • 没有重试/断路器的跨服务 HTTP 调用
  • 支付相关端点缺少幂等键
  • 队列消费者没有死信队列(DLQ)—— 坏消息反复消费
  • 同步调用链 > 3 层(请求串行穿透多个服务)
  • 从表现层直接调用数据层(跳过 Business/Service)

输出模板

服务架构设计完成后,应在 ADR 或 03-plan.md 中记录以下结构:

## Service Architecture — [功能名称]

### 服务边界
| 服务 | 业务领域 | 负责范围 |
|------|---------|---------|
| [服务 A] | [领域] | [职责描述] |
| [服务 B] | [领域] | [职责描述] |

### 通信模式
| 调用路径 | 模式 | 理由 |
|---------|------|------|
| [A → B] | 同步 / 异步 / Event / Saga | [需要立即结果 / 可延迟 / 多消费者 / 跨服务长事务] |

### 韧性配置
| 调用路径 | 重试策略 | 断路器 | 幂等性 | 超时 |
|---------|---------|--------|-------|------|
| [A → B] | [次数 + 退避] | [阈值] | [是否需要] | [毫秒值] |

| 队列 | 消费者 | 死信队列 | 重试次数 |
|------|--------|---------|---------|
| [Queue A] | [消费者] | [DLQ 配置] | [次数] |

### 模式选择
| 需求 | 选定模式 | 复杂度代价 | 选择理由 |
|------|---------|-----------|---------|
| [需求描述] | [模式名] | [低/中/高] | [当前需求最简方案] |

### 故障模式
| 单点 | 降级方案 | 影响范围 |
|------|---------|---------|
| [单点描述] | [降级策略] | [受影响服务/用户] |

验证清单

  • 服务边界清晰(按业务领域,不按技术层)
  • 跨服务调用有重试和超时配置
  • 支付/扣款相关有幂等键
  • 异步消费者有死信队列
  • 故障模式已识别(单点在哪?降级方案?)
  • 选择的是当前需要的最简模式
Install via CLI
npx skills add https://github.com/ZeroZ-lab/unified-skills --skill build-backend-service-patterns
Repository Details
star Stars 15
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator