# 单体到微服务演进导论

::: tip 前言
**没有哪个架构是"最好的"，只有"最适合当前阶段的"。** 从单体到微服务不是一步到位的跳跃，而是随着业务规模和团队规模增长，逐步演进的过程。过早拆分微服务和过晚拆分一样危险。
:::

**这篇文章会带你学什么？**

学完这章后，你将获得：

- **演进路径**：理解从单体到微服务的四个阶段
- **拆分时机**：知道什么时候该拆、什么时候不该拆
- **拆分策略**：掌握按业务域拆分的方法论
- **通信模式**：了解服务间同步和异步通信的选择
- **数据拆分**：理解数据库拆分的挑战和方案

| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 架构演进路径 | 单体→模块化→SOA→微服务 |
| **第 2 章** | 拆分时机与原则 | Conway 定律、团队自治 |
| **第 3 章** | 拆分策略 | DDD 限界上下文、绞杀者模式 |
| **第 4 章** | 服务通信 | REST、gRPC、消息队列 |
| **第 5 章** | 数据拆分 | 数据库拆分、数据同步 |

---

## 1. 架构演进路径

架构演进不是技术驱动的，而是**组织规模驱动的**。当团队从 5 人增长到 500 人时，单体架构的协作效率会急剧下降。

| 阶段 | 架构 | 团队规模 | 特点 |
|------|------|---------|------|
| 起步期 | 单体应用 | 1~10 人 | 所有代码在一个项目中，部署简单 |
| 成长期 | 模块化单体 | 10~50 人 | 代码按模块划分，但仍然一起部署 |
| 扩张期 | SOA（面向服务） | 50~200 人 | 按业务线拆分为粗粒度服务 |
| 规模期 | 微服务 | 200+ 人 | 细粒度服务，每个团队独立开发部署 |

<ArchEvolutionDemo />

::: tip Conway 定律
"设计系统的组织，其产生的架构等同于组织的沟通结构。"——Melvin Conway

简单说：3 个团队做一个系统，最终会变成 3 个服务。架构拆分的本质是**组织拆分**。

**反向 Conway 定律**：既然组织结构决定了系统架构，那么想要什么样的架构，就先调整成什么样的组织结构。比如你想拆出独立的支付服务，就先组建一个独立的支付团队。很多公司微服务拆分失败，不是技术问题，而是组织没有跟着调整。
:::

---

## 2. 什么时候该拆微服务

不是所有系统都需要微服务。过早拆分会带来不必要的复杂性。

| 信号 | 说明 | 建议 |
|------|------|------|
| 部署冲突频繁 | 多个团队改同一个代码库，经常冲突 | 考虑拆分 |
| 某模块需要独立扩容 | 搜索模块需要 10 倍于其他模块的资源 | 考虑拆分 |
| 技术栈需要差异化 | AI 模块用 Python，主站用 Java | 考虑拆分 |
| 团队 < 10 人 | 沟通成本低，单体足够 | 不要拆 |
| 业务还在探索期 | 需求变化快，边界不清晰 | 不要拆 |
| 没有 DevOps 能力 | 没有 CI/CD、容器化、监控体系 | 不要拆 |

---

## 3. 拆分策略

### 3.1 按业务域拆分（DDD 限界上下文）

DDD（领域驱动设计）的限界上下文（Bounded Context）是拆分微服务的最佳指导原则。每个限界上下文对应一个独立的业务域，有自己的数据模型和业务规则。

**什么是限界上下文？** 同一个词在不同业务域中含义不同。比如"用户"在用户域是指注册信息（姓名、邮箱），在订单域是指下单人（收货地址、支付方式），在推荐域是指行为画像（浏览历史、偏好标签）。限界上下文就是划定一个边界，在这个边界内，术语和模型有明确统一的含义。

```
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   用户域     │  │   订单域     │  │   支付域     │
│             │  │             │  │             │
│ User        │  │ Order       │  │ Payment     │
│ Profile     │  │ OrderItem   │  │ Refund      │
│ Address     │  │ Cart        │  │ Transaction │
│             │  │             │  │             │
│ 用户服务    │  │ 订单服务     │  │ 支付服务    │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────── API 调用 / 事件通信 ───────┘
```

| 限界上下文 | 核心实体 | 对应服务 |
|-----------|---------|---------|
| 用户域 | User、Profile、Address | 用户服务 |
| 商品域 | Product、Category、SKU | 商品服务 |
| 订单域 | Order、OrderItem | 订单服务 |
| 支付域 | Payment、Refund | 支付服务 |
| 物流域 | Shipment、Tracking | 物流服务 |

### 3.2 绞杀者模式（Strangler Fig Pattern）

不要一次性重写整个单体，而是像绞杀榕一样，逐步用新服务替换旧模块：

1. 在单体外部创建新服务
2. 通过代理层将部分流量路由到新服务
3. 验证新服务稳定后，逐步迁移更多流量
4. 最终完全替换旧模块

---

## 4. 服务通信模式

| 方式 | 协议 | 特点 | 适用场景 |
|------|------|------|---------|
| REST | HTTP/JSON | 简单通用，生态好 | 对外 API、CRUD 操作 |
| gRPC | HTTP/2 + Protobuf | 高性能，强类型 | 内部服务间高频调用 |
| 消息队列 | AMQP/Kafka | 异步解耦，削峰填谷 | 事件通知、异步任务 |
| GraphQL | HTTP/JSON | 客户端按需查询 | BFF 层、移动端 |

::: tip 同步 vs 异步的选择
- **需要立即返回结果** → 同步（REST/gRPC）
- **不需要立即返回** → 异步（消息队列）
- **一个事件触发多个动作** → 异步（发布-订阅）

经验法则：能异步就异步，同步调用链越长，系统越脆弱。
:::

---

## 5. 数据拆分：最难的部分

微服务拆分中最痛苦的不是代码拆分，而是数据库拆分。每个服务应该拥有自己的数据库，但这意味着跨服务查询变得困难。

| 挑战 | 描述 | 解决方案 |
|------|------|---------|
| 跨服务 JOIN | 不能直接 JOIN 两个服务的表 | API 组合查询、数据冗余 |
| 分布式事务 | 跨库事务无法用本地事务 | Saga、本地消息表 |
| 数据一致性 | 多个服务的数据可能暂时不一致 | 最终一致性、事件驱动 |
| 数据迁移 | 从共享库迁移到独立库 | 双写过渡、数据同步工具 |

---

## 总结

从单体到微服务是一个渐进的过程，不是一蹴而就的革命。

回顾本章的关键要点：

1. **演进路径**：单体→模块化单体→SOA→微服务，每一步都有明确的驱动力
2. **拆分时机**：团队规模、部署冲突、扩容需求是拆分的信号
3. **拆分策略**：用 DDD 限界上下文指导拆分，用绞杀者模式渐进迁移
4. **通信选择**：能异步就异步，同步调用链越短越好
5. **数据拆分**：最难但最重要，接受最终一致性是关键心态转变

## 延伸阅读

- [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) - Sam Newman 微服务经典
- [Monolith to Microservices](https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/) - 渐进式迁移指南
- [Domain-Driven Design](https://www.domainlanguage.com/ddd/) - Eric Evans 的 DDD 经典
- [The Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html) - Martin Fowler 的绞杀者模式

