Skip to content

服务拆分原则

一、何时拆分微服务?

单体 vs 微服务

┌─────────────────────────────────────────────────────────────────┐
│                   单体架构 vs 微服务架构                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   单体架构                        微服务架构                     │
│   ┌─────────────────────┐        ┌─────┐ ┌─────┐ ┌─────┐       │
│   │                     │        │用户 │ │订单 │ │支付 │       │
│   │   用户模块           │        │服务 │ │服务 │ │服务 │       │
│   │   ─────────         │        └──┬──┘ └──┬──┘ └──┬──┘       │
│   │   订单模块           │           │       │       │          │
│   │   ─────────         │           ▼       ▼       ▼          │
│   │   支付模块           │        ┌─────────────────────┐       │
│   │   ─────────         │        │    消息队列/网关    │       │
│   │                     │        └─────────────────────┘       │
│   └─────────────────────┘                                      │
│                                                                 │
│   优势:                          优势:                          │
│   • 简单、开发快                  • 独立部署、独立扩展            │
│   • 本地调用、无网络开销          • 技术栈灵活                    │
│   • 事务简单                     • 故障隔离                      │
│   • 运维简单                     • 团队自治                      │
│                                                                 │
│   劣势:                          劣势:                          │
│   • 扩展受限                     • 分布式复杂性                   │
│   • 技术栈固定                   • 运维成本高                    │
│   • 故障影响全局                  • 数据一致性难                  │
│   • 团队协作困难                  • 网络开销                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

拆分时机判断

┌─────────────────────────────────────────────────────────────────┐
│                    是否需要拆分微服务?                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ✅ 适合拆分的场景:                                             │
│   ────────────────────────────────────────                      │
│   □ 团队规模 > 10 人,协作困难                                   │
│   □ 不同模块需要独立扩展 (如搜索需要 20 实例,用户只需 2 个)      │
│   □ 不同模块技术栈需求不同 (如 AI 模块用 Python)                 │
│   □ 需要独立发布,减少发布风险                                   │
│   □ 故障需要隔离,核心业务不受非核心影响                          │
│   □ 有明确的业务边界 (如电商: 用户、商品、订单、支付)             │
│                                                                 │
│   ❌ 不适合拆分的场景:                                           │
│   ────────────────────────────────────────                      │
│   □ 团队规模 < 5 人                                             │
│   □ 业务还在快速变化,边界不清晰                                 │
│   □ 单体性能足够,没有扩展需求                                   │
│   □ 没有分布式基础设施 (服务发现、网关、监控)                     │
│   □ 团队没有微服务经验                                          │
│                                                                 │
│   ⚠️ 提醒: 微服务不是银弹                                        │
│   ────────────────────────────────────────                      │
│   "如果你的单体架构做得不好,微服务只会让它更糟糕"                 │
│   - 先优化单体,再考虑拆分                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

二、拆分原则

1. DDD 领域驱动设计

┌─────────────────────────────────────────────────────────────────┐
│                   基于领域驱动的服务拆分                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Step 1: 识别限界上下文 (Bounded Context)                       │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │    电商系统领域                                           │  │
│   │    ┌────────────┐ ┌────────────┐ ┌────────────┐         │  │
│   │    │  用户上下文 │ │  商品上下文 │ │  订单上下文 │         │  │
│   │    │            │ │            │ │            │         │  │
│   │    │ • 用户     │ │ • 商品     │ │ • 订单     │         │  │
│   │    │ • 地址     │ │ • 分类     │ │ • 订单项   │         │  │
│   │    │ • 会员     │ │ • 库存     │ │ • 物流     │         │  │
│   │    └────────────┘ └────────────┘ └────────────┘         │  │
│   │                                                          │  │
│   │    ┌────────────┐ ┌────────────┐ ┌────────────┐         │  │
│   │    │  支付上下文 │ │  营销上下文 │ │  搜索上下文 │         │  │
│   │    │            │ │            │ │            │         │  │
│   │    │ • 支付单   │ │ • 优惠券   │ │ • 索引     │         │  │
│   │    │ • 退款     │ │ • 活动     │ │ • 搜索     │         │  │
│   │    │ • 对账     │ │ • 积分     │ │ • 推荐     │         │  │
│   │    └────────────┘ └────────────┘ └────────────┘         │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   Step 2: 一个限界上下文 = 一个微服务                            │
│   ────────────────────────────────────────                      │
│   用户上下文 → user-service                                     │
│   商品上下文 → product-service                                  │
│   订单上下文 → order-service                                    │
│   支付上下文 → payment-service                                  │
│   ...                                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 单一职责原则

┌─────────────────────────────────────────────────────────────────┐
│                   服务的单一职责                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ❌ 错误: 服务职责过多                                          │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │   order-service                                          │  │
│   │   ├─ 订单管理                                            │  │
│   │   ├─ 库存扣减      ← 应该属于 inventory-service          │  │
│   │   ├─ 支付处理      ← 应该属于 payment-service            │  │
│   │   ├─ 物流查询      ← 应该属于 logistics-service          │  │
│   │   └─ 发送通知      ← 应该属于 notification-service       │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   ✅ 正确: 职责单一                                              │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │   order-service                                          │  │
│   │   ├─ 创建订单                                            │  │
│   │   ├─ 查询订单                                            │  │
│   │   ├─ 取消订单                                            │  │
│   │   └─ 订单状态管理                                        │  │
│   │                                                          │  │
│   │   调用其他服务:                                          │  │
│   │   ├─ inventory-service.deduct()   扣减库存               │  │
│   │   ├─ payment-service.create()     创建支付               │  │
│   │   └─ notification-service.send()  发送通知               │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   判断标准: 修改一个功能时,是否只需要改一个服务?                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3. 服务自治原则

┌─────────────────────────────────────────────────────────────────┐
│                      服务自治原则                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   1. 数据自治 - 每个服务有自己的数据库                           │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   ❌ 共享数据库                    ✅ 数据库隔离           │  │
│   │   ┌─────────┐  ┌─────────┐      ┌─────────┐  ┌─────────┐│  │
│   │   │ Service │  │ Service │      │ Service │  │ Service ││  │
│   │   │    A    │  │    B    │      │    A    │  │    B    ││  │
│   │   └────┬────┘  └────┬────┘      └────┬────┘  └────┬────┘│  │
│   │        │            │                │            │     │  │
│   │        └─────┬──────┘                ▼            ▼     │  │
│   │              ▼                  ┌─────────┐  ┌─────────┐│  │
│   │         ┌─────────┐            │   DB    │  │   DB    ││  │
│   │         │   DB    │            │    A    │  │    B    ││  │
│   │         └─────────┘            └─────────┘  └─────────┘│  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   2. 部署自治 - 独立部署,不影响其他服务                         │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   发布 order-service v2.0                                │  │
│   │   ├─ user-service    不受影响 ✓                         │  │
│   │   ├─ product-service 不受影响 ✓                         │  │
│   │   └─ payment-service 不受影响 ✓                         │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   3. 技术自治 - 可以选择不同的技术栈                             │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   user-service     → Go + MySQL                         │  │
│   │   search-service   → Java + Elasticsearch                │  │
│   │   ml-service       → Python + TensorFlow                 │  │
│   │   realtime-service → Node.js + Redis                     │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

三、拆分策略

1. 绞杀者模式 (Strangler Fig Pattern)

┌─────────────────────────────────────────────────────────────────┐
│                    绞杀者模式演进                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   阶段 1: 在单体旁边部署新服务                                   │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   用户 ──▶ 负载均衡                                       │  │
│   │              │                                           │  │
│   │              ├──▶ 单体应用 (所有功能)                     │  │
│   │              │    ├─ 用户模块                            │  │
│   │              │    ├─ 订单模块                            │  │
│   │              │    └─ 支付模块                            │  │
│   │              │                                           │  │
│   │              └──▶ 新用户服务 (独立出来)                   │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   阶段 2: 逐步迁移流量                                          │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   /api/users/* ──▶ 新用户服务 (100%)                     │  │
│   │   /api/orders/* ──▶ 单体应用                              │  │
│   │   /api/payments/* ──▶ 单体应用                           │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   阶段 3: 持续拆分,直到单体消失                                 │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   /api/users/*    ──▶ user-service                       │  │
│   │   /api/orders/*   ──▶ order-service                      │  │
│   │   /api/payments/* ──▶ payment-service                    │  │
│   │                                                          │  │
│   │   单体应用 (已废弃)                                       │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 分支抽象模式 (Branch by Abstraction)

go
// Step 1: 定义抽象接口
type UserRepository interface {
    GetByID(id string) (*User, error)
    Create(user *User) error
}

// Step 2: 实现旧版本 (访问本地数据库)
type LocalUserRepository struct {
    db *sql.DB
}

func (r *LocalUserRepository) GetByID(id string) (*User, error) {
    return r.db.Query("SELECT * FROM users WHERE id = ?", id)
}

// Step 3: 实现新版本 (调用新服务)
type RemoteUserRepository struct {
    client UserServiceClient
}

func (r *RemoteUserRepository) GetByID(id string) (*User, error) {
    return r.client.GetUser(context.Background(), id)
}

// Step 4: 使用特性开关切换
type SwitchableUserRepository struct {
    local  UserRepository
    remote UserRepository
    toggle FeatureToggle
}

func (r *SwitchableUserRepository) GetByID(id string) (*User, error) {
    if r.toggle.IsEnabled("use_remote_user_service") {
        return r.remote.GetByID(id)
    }
    return r.local.GetByID(id)
}

// Step 5: 全量切换后,删除旧实现

四、数据拆分

1. 数据迁移策略

┌─────────────────────────────────────────────────────────────────┐
│                      数据拆分策略                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   方式 1: 共享数据库 → 逻辑隔离                                  │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   同一数据库,不同 Schema                                 │  │
│   │   ┌─────────────────────────────────────────────────┐   │  │
│   │   │                   MySQL                          │   │  │
│   │   │  ┌───────────┐ ┌───────────┐ ┌───────────┐     │   │  │
│   │   │  │ user_db   │ │ order_db  │ │ payment_db│     │   │  │
│   │   │  │ (Schema)  │ │ (Schema)  │ │ (Schema)  │     │   │  │
│   │   │  └───────────┘ └───────────┘ └───────────┘     │   │  │
│   │   └─────────────────────────────────────────────────┘   │  │
│   │                                                          │  │
│   │   优点: 简单、事务可跨 Schema                             │  │
│   │   缺点: 仍然耦合、资源竞争                                │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   方式 2: 物理隔离                                               │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   ┌───────────┐   ┌───────────┐   ┌───────────┐        │  │
│   │   │ MySQL     │   │ MySQL     │   │ MySQL     │        │  │
│   │   │ user_db   │   │ order_db  │   │ payment_db│        │  │
│   │   └───────────┘   └───────────┘   └───────────┘        │  │
│   │                                                          │  │
│   │   优点: 完全隔离、独立扩展                                │  │
│   │   缺点: 跨服务查询困难、事务复杂                          │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 跨服务查询

go
// 问题: 如何实现跨服务的联表查询?
// 原来: SELECT * FROM orders o JOIN users u ON o.user_id = u.id

// 方案 1: API 组合
func GetOrderWithUser(orderID string) (*OrderWithUser, error) {
    // 1. 查询订单
    order, err := orderService.GetOrder(orderID)
    if err != nil {
        return nil, err
    }

    // 2. 查询用户
    user, err := userService.GetUser(order.UserID)
    if err != nil {
        return nil, err
    }

    return &OrderWithUser{
        Order: order,
        User:  user,
    }, nil
}

// 方案 2: 数据冗余
// 订单表中冗余用户信息
type Order struct {
    ID       string
    UserID   string
    UserName string  // 冗余字段
    // ...
}

// 方案 3: CQRS - 读写分离
// 写: 分散在各个服务
// 读: 构建统一的查询模型
type OrderQueryService struct {
    esClient *elasticsearch.Client
}

func (s *OrderQueryService) SearchOrders(query SearchQuery) ([]OrderView, error) {
    // 从 ES 查询,ES 中已包含订单+用户+商品信息
    return s.esClient.Search(query)
}

// 通过事件同步数据到 ES
func (s *OrderQueryService) OnOrderCreated(event OrderCreatedEvent) {
    orderView := buildOrderView(event)
    s.esClient.Index(orderView)
}

五、服务通信

1. 同步 vs 异步

┌─────────────────────────────────────────────────────────────────┐
│                   服务通信方式选择                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   同步通信 (HTTP/gRPC)                                          │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   Service A ──────请求──────▶ Service B                  │  │
│   │             ◀─────响应───────                            │  │
│   │                                                          │  │
│   │   适用场景:                                               │  │
│   │   • 需要立即返回结果 (如: 查询用户信息)                    │  │
│   │   • 调用链短 (2-3 层)                                    │  │
│   │   • 强一致性要求                                         │  │
│   │                                                          │  │
│   │   缺点:                                                   │  │
│   │   • 服务耦合                                             │  │
│   │   • 被调用方故障影响调用方                                │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   异步通信 (消息队列)                                           │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │   Service A ──────发送消息──────▶ MQ ──▶ Service B       │  │
│   │             (不等待响应)                                  │  │
│   │                                                          │  │
│   │   适用场景:                                               │  │
│   │   • 不需要立即返回结果 (如: 发送通知)                     │  │
│   │   • 解耦服务                                             │  │
│   │   • 削峰填谷                                             │  │
│   │   • 最终一致性可接受                                     │  │
│   │                                                          │  │
│   │   缺点:                                                   │  │
│   │   • 复杂度增加                                           │  │
│   │   • 消息顺序、重复处理                                    │  │
│   │                                                          │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 事件驱动架构

go
// 事件定义
type OrderCreatedEvent struct {
    OrderID   string    `json:"order_id"`
    UserID    string    `json:"user_id"`
    Items     []Item    `json:"items"`
    Amount    float64   `json:"amount"`
    CreatedAt time.Time `json:"created_at"`
}

// 订单服务 - 发布事件
type OrderService struct {
    repo      OrderRepository
    publisher EventPublisher
}

func (s *OrderService) CreateOrder(order *Order) error {
    // 保存订单
    if err := s.repo.Save(order); err != nil {
        return err
    }

    // 发布事件
    event := OrderCreatedEvent{
        OrderID:   order.ID,
        UserID:    order.UserID,
        Items:     order.Items,
        Amount:    order.Amount,
        CreatedAt: time.Now(),
    }
    return s.publisher.Publish("order.created", event)
}

// 库存服务 - 订阅事件
type InventoryService struct {
    repo       InventoryRepository
    subscriber EventSubscriber
}

func (s *InventoryService) Start() {
    s.subscriber.Subscribe("order.created", s.handleOrderCreated)
}

func (s *InventoryService) handleOrderCreated(event OrderCreatedEvent) error {
    for _, item := range event.Items {
        if err := s.repo.Deduct(item.ProductID, item.Quantity); err != nil {
            return err
        }
    }
    return nil
}

// 通知服务 - 也订阅同一事件
type NotificationService struct {
    sender     EmailSender
    subscriber EventSubscriber
}

func (s *NotificationService) Start() {
    s.subscriber.Subscribe("order.created", s.handleOrderCreated)
}

func (s *NotificationService) handleOrderCreated(event OrderCreatedEvent) error {
    return s.sender.SendOrderConfirmation(event.UserID, event.OrderID)
}

六、拆分检查清单

拆分前评估

  • [ ] 团队规模是否支持微服务?
  • [ ] 业务边界是否清晰?
  • [ ] 基础设施是否准备好?
  • [ ] 团队是否有微服务经验?

拆分过程检查

  • [ ] 是否基于业务领域拆分?
  • [ ] 每个服务是否职责单一?
  • [ ] 服务之间是否松耦合?
  • [ ] 数据是否独立?

拆分后验证

  • [ ] 服务可以独立部署吗?
  • [ ] 单个服务故障是否影响其他服务?
  • [ ] 服务可以独立扩展吗?
  • [ ] 团队可以自主开发部署吗?

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布