Microservice or Monolith(微服务还是单体)
解决什么问题:怎么判断一个系统应该从单体起步还是直接拆微服务—— 拍脑袋选 = 拆早了运维崩溃、拆晚了耦合到拆不动。 选错了 = 几年回炉重写。
它是什么
Monolith(单体)——一个部署单元、一个进程、一个数据库。所有模块共享同一代码库、同一运行时。
Microservice(微服务)——每个服务独立部署、独立进程、可选独立数据库,服务间通过 API / 消息通信。
这不是二选一——存在中间形态:
1. 单体(Modular Monolith) ← 推荐起步
2. 单体 + 模块化边界 ← 推荐 10 人团队
3. 部分模块独立部署(绞杀者模式) ← 推荐过渡
4. 微服务(完整拆分) ← 推荐 ≥ 50 人 / 多团队Sam Newman(Building Microservices):"Premature decomposition is a big risk. Most successful microservice extractions started as a modular monolith and were split later."
V2 视角(V2.7 B3 段 5 + B6 段 4):这是 L2 设计层最经典的折衷——没有"哪个绝对好",只有"在你这个规模 / 团队 / 业务下哪个更划算"。
关键属性
| 属性 | Modular Monolith | Microservices |
|---|---|---|
| 部署单元 | 1 个 | N 个(N 个 BC) |
| 代码库 | 1 个仓库 / 多模块 | N 个仓库 或 monorepo |
| 数据库 | 1 个 | 每个服务独立 DB(推荐) |
| 通信 | 进程内方法调用 | HTTP / gRPC / 消息 |
| 部署频率 | 一起部署 | 独立部署 |
| 故障域 | 1 个(全局故障) | N 个(局部故障) |
| 运维成本 | 低 | 高(K8s/服务网格/监控) |
| 团队规模 | ≤ 20 人 | ≥ 50 人 |
| 业务成熟度 | 早期 / 探索期 | 稳定期 / 多团队并行 |
怎么用
场景 1:起步选型决策树
Q1: 团队规模?
├─ ≤ 5 人 → Modular Monolith(必须)
├─ 5-20 人 → Modular Monolith(强烈推荐)
└─ ≥ 50 人 → 继续问
Q2: 业务稳定吗?
├─ 还在探索 → Modular Monolith(先跑通业务)
└─ 稳定 + 多个独立 BU → Microservices
Q3: 部署频率需求?
├─ 周级部署 → Monolith 足够
└─ 日级 / 小时级 → 考虑拆分(按 BC)
Q4: 团队地理分布?
├─ 同城 / 同楼 → Monolith 协作方便
└─ 多城市 / 多时区 → Microservices(解耦协作)
Q5: 性能 / 伸缩粒度?
├─ 整体伸缩 → Monolith
└─ 单模块 10x 伸缩 → Microservices(只扩热的那个)Vernon 在 IDDD Ch 3 也明确:"在大多数项目中,先用单体起步,等 BC 稳定后再拆分"。
场景 2:Modular Monolith 的工程纪律
V2 B3 段 5 "团队可拥有" 视角:Modular Monolith 成功的关键是强制模块边界——
com.example.app/
├── order/ ← Order BC 模块
│ ├── domain/ ← 聚合、用例
│ ├── application/ ← 应用服务
│ ├── infrastructure/ ← Adapter
│ └── interfaces/ ← 本模块 Web / API
│
├── payment/ ← Payment BC 模块
│ ├── domain/
│ └── ...
│
└── shared/ ← 跨 BC 共享(慎用!)
└── kernel/ ← 共享内核必须明确纪律工具:
- Maven / Gradle 多模块:每个 BC 一个 module,强制依赖方向
- ArchUnit 单元测试——禁止
order.domain依赖payment.domain - 包私有可见性——BC 内 public,BC 间用专门 Facade
- 定期 Event Storming——确保 BC 边界清晰
场景 3:绞杀者模式(Strangler Fig)—— 平滑迁移
从单体拆微服务的标准方法——逐步替换,不重写:
┌──────────┐
老请求 → ──→ │ Facade │ ──→ 老单体
│(路由层) │ ──→ 新服务 A (已迁)
│ │ ──→ 新服务 B (已迁)
└──────────┘步骤:
- 在入口加 Facade(API 网关 / Nginx / 业务路由层)
- 把一个 BC 的流量切到新服务(10% → 50% → 100%)
- 老代码里删掉这个 BC 的代码
- 重复下一个 BC
SE@G Ch 17:"演化式拆分(Evolutionary Decomposition)"——永远不要一次性拆完。
场景 4:拆分时的反模式
分布式单体(Distributed Monolith)——最常见的拆错:
❌ "微服务" 但:
- 共享同一个数据库
- 用同步 HTTP 调用串成链路
- 一起部署
- 一个服务挂了全链路挂
= 这不是微服务,是"用 RPC 调用的单体",更糟糕V2 B6 段 5 "折衷的退化":拆分要保证独立部署 + 独立伸缩 + 独立故障域——三个都满足才算真拆开。
场景 5:判断"什么时候该拆"
团队规模 × 协作成本 触发拆分的阈值:
- 5 人 → 永远不该拆(运维成本 > 收益)
- 10-20 人 → 看情况(构建慢 → 拆构建;部署耦合 → 拆部署)
- 50 人+ → 必须拆(否则 PR 冲突、部署互锁)
业务演化触发拆分的阈值:
- 一个 BC 的变更频率 > 其它 10x → 拆(让热的独立部署)
- 一个 BC 的伸缩需求 > 其它 10x → 拆(独立扩缩容)
- 多个团队负责不同 BC → 拆(团队边界对齐服务边界)常见误区
- "微服务 = 高级"——错。Monolith 是 90% 系统的正解,过早拆微服务 = 自找麻烦
- "拆了微服务就能独立部署"——半对。没拆干净 = 分布式单体,比单体更糟
- "先拆微服务再说"——错。先跑通业务 + 识别 BC + 摸清边界,再拆——否则拆出来的 BC 边界也是错的
- "微服务必须用 K8s + 服务网格 + 分布式追踪"——错。5 个服务的微服务用 docker-compose 就够。工具按规模来
- "一旦单体就永远单体"——错。绞杀者模式 + 演化拆分,随时可以从单体过渡到微服务
关键洞察
"先单体再拆分"是 90% 情况下的正解;"一开始拆"是少数情况下的正解。
选错的核心原因是用技术时髦度代替业务判断—— 微服务是工具,不是目标。
V2 B6 折衷原则:团队规模、业务稳定度、部署频率、伸缩需求 四个变量决定起点。"Microservice or Monolith" 不是信仰选择, 是工程权衡。判错标志 = 团队人数 5 但在跑 K8s 集群 / 团队 100 但还在一个 500 万行单体里。
相关链接
- Clean / Hexagonal——无论选哪种都要内部 Clean
- Views & Decisions——拆分决策必须 ADR 化
- API Design——微服务间的契约
- Event Contract——微服务间的异步集成
- [V3 涌现作为] · 分解——分解是微服务的前置条件
- [V2 B6 折衷] · 折衷——单体 vs 微服务的折衷