Skip to content
第 8 章 架构 ⏱ 13 分钟阅读

第 8 章:微服务拆分 ​

学习目标 ​

  • 掌握 4 种主流拆分方法(业务、领域、DDD、组织)
  • 学会按业务能力优先级排定拆分顺序
  • 理解绞杀者模式和修缮模式
  • 避开拆分粒度过细、过早拆分的坑

一、4 种拆分方法 ​

1. 按业务能力拆分 ​

text
电商业务能力
├── 用户管理(注册/登录/资料)
├── 商品管理(上架/分类)
├── 交易管理(购物车/订单)
├── 支付管理(收款/退款)
├── 物流管理(发货/配送)
└── 营销管理(优惠券/促销)
java
// 一个业务能力 = 一个服务
public class TradeService {
    public Cart createCart(UserId userId) { ... }
    public OrderId submitOrder(Cart cart) { ... }
}

2. 按子域拆分(DDD 拆分) ​

text
核心子域
├── 商品(定价/库存)
└── 订单(下单/支付)

支撑子域
├── 物流(发货/配送)
└── 客服(投诉/售后)

通用子域
├── 认证
└── 通知

3. 按组织拆分(康威定律) ​

text
用户团队 → 用户服务
商品团队 → 商品服务
订单团队 → 订单服务
基础设施组 → 通用组件

4. 按技术能力拆分 ​

text
├── 高 QPS 模块独立(秒杀)
├── 慢查询模块独立(报表)
└── 计算密集型独立(推荐)

⚠️ 坑 1:按"数据表"拆,出现 UserService、OrderService、AddressService、CouponService — 后期 50 个服务互相调用,链路长到失控。按业务能力拆,不是按表。

二、拆分顺序:从易到难 ​

text
阶段 1:基础设施独立
  ├── 认证
  ├── 通知
  └── 配置中心

阶段 2:通用模块独立
  ├── 用户
  ├── 权限
  └── 文件

阶段 3:核心业务独立
  ├── 商品
  ├── 订单
  └── 支付

阶段 4:营销与扩展
  ├── 优惠券
  ├── 推荐
  └── 搜索
java
// 阶段 1:认证服务独立
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @PostMapping("/login")
    public TokenDto login(@RequestBody LoginRequest req) {
        return authService.login(req.username(), req.password());
    }
}

先拆不会引入分布式事务的模块(用户、认证),最后拆事务重的(订单、支付)。

⚠️ 坑 2:第一个就拆"订单+支付+库存",三大分布式事务叠在一起,直接放弃。先拆没"强一致性"的,后拆"必须强一致"的。

三、绞杀者模式(Strangler Fig) ​

逐步替换旧单体,而不是一次性重写:

text
阶段 1:100% 单体
[
  旧单体
]

阶段 2:新功能走新服务
[
  旧单体        ]
  用户服务(新)
]

阶段 3:逐步迁移
[
  旧单体   ]
  用户服务
  商品服务      ← API 网关分流
]

阶段 4:旧单体萎缩到 0
[
  用户服务
  商品服务
  订单服务
]
java
// API 网关分流
spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://user-service
          predicates:
            - Path=/api/user/**

        - id: old_route
          uri: lb://legacy-monolith
          predicates:
            - Path=/api/old/**

⚠️ 坑 3:"大爆炸(Big Bang)"重写,从 0 写新系统替换旧系统,几乎一定失败。IBM、Microsoft、淘宝都失败过。绞杀者模式渐进迁移。

四、修缮模式 ​

不去替换旧堡垒,在旁边建新房:

java
// 旧系统继续维护
@Service
public class LegacyOrderService {
    public void placeOrder(OrderDto dto) { ... }
}

// 新功能建新房
@Service
public class NewOrderService {
    public void placeOrder(OrderDto dto) {
        // 1. 调旧接口下单
        legacyOrderService.placeOrder(dto);
        // 2. 调用新的库存服务
        inventoryService.deduct(dto.items());
        // 3. 发送新事件
        publisher.publishEvent(new OrderPlacedV2Event(dto));
    }
}

五、识别依赖关系 ​

准备拆分前,画依赖图:

text
[订单] ──→ [商品]
   ↓
[支付] ──→ [用户]
   ↓
[库存] ──→ [商品]
java
// 工具:用代码扫描依赖
public class DependencyAnalyzer {
    public Map<String, Set<String>> analyze(String projectPath) {
        // 扫描 @Resource / @Autowired / @FeignClient
        // 输出每个 class 被哪些引用
    }
}

⚠️ 坑 4:依赖分析发现"订单"被 30 个类依赖,先抽调用方再抽被调方。先拆依赖少的,后拆依赖多的。

六、拆分后的常见问题 ​

java
// 问题 1:分布式事务
// 单体 @Transactional 就行,跨服务需要 Saga/TCC/Seata

// 问题 2:跨服务查询
// 原 SQL JOIN 失效,改成 API 编排
public OrderDetailDto getOrderDetail(OrderId id) {
    Order order = orderClient.findById(id);
    User user = userClient.findById(order.buyerId());
    List<OrderItem> items = order.items();
    return new OrderDetailDto(order, user, items);
}

// 问题 3:数据一致性
// 只能最终一致,需要补偿机制(对账、重试)

本章小结 ​

方法适用场景
业务能力业务清晰,标准能力
子域(DDD)复杂业务,需要长期演进
组织团队规模大,边界清晰
技术能力性能瓶颈模块
模式策略
绞杀者渐进替换,新旧并存
修缮保留旧系统,边上建新
大爆炸❌ 几乎必定失败

动手练习 ​

  1. 画依赖图:用工具扫描你手头的项目,画出"模块 → 模块"调用图
  2. 拆分演示:把"用户管理"独立成一个服务,API 网关分流旧路径
  3. 绞杀者:把一个老接口 /api/oldOrder 切到新服务 /api/order,观察过渡期流量
  4. 优先级:用上述 4 种方法各拆一次,合并去重后排出 5 个阶段

下一章:第 9 章:服务注册发现 →

本站基于 VitePress 构建 · 由 StackHub 团队维护