跳转至

第二章:服务拆分策略与边界划分

2.1 服务拆分的核心原则

服务拆分是微服务架构设计中最关键的决策之一。拆分得当,系统灵活可维护;拆分不当,系统复杂混乱。

拆分原则

  1. 单一职责:每个服务只负责一个业务能力
  2. 高内聚低耦合:服务内部紧密关联,服务间松散耦合
  3. 业务优先:按业务边界拆分,而非技术边界
  4. 数据独立:每个服务管理自己的数据
  5. 团队对齐:服务边界与团队组织对齐

2.2 领域驱动设计(DDD)方法

限界上下文(Bounded Context)

限界上下文是 DDD 中定义服务边界的核心概念:

┌─────────────────────────────────────────────────────────┐
│                    电商系统领域模型                       │
├─────────────────┬─────────────────┬─────────────────────┤
│   用户上下文     │   订单上下文     │   商品上下文         │
│  ┌───────────┐  │  ┌───────────┐  │  ┌───────────────┐  │
│  │  User     │  │  │  Order    │  │  │  Product      │  │
│  │  Account  │  │  │  OrderItem│  │  │  Category     │  │
│  │  Address  │  │  │  Payment  │  │  │  Inventory    │  │
│  └───────────┘  │  └───────────┘  │  └───────────────┘  │
│   用户服务       │   订单服务       │   商品服务           │
└─────────────────┴─────────────────┴─────────────────────┘

识别限界上下文的方法

# 1. 事件风暴(Event Storming)识别领域事件

# 领域事件示例
events = [
    "用户已注册",
    "商品已上架",
    "订单已创建",
    "支付已完成",
    "库存已扣减",
    "订单已发货",
    "订单已完成",
]

# 2. 根据事件识别聚合和边界
# 用户注册 → 用户上下文
# 商品上架 → 商品上下文
# 订单创建 → 订单上下文

聚合(Aggregate)设计

# 订单聚合示例
class Order:
    """订单聚合根"""
    def __init__(self, order_id, user_id):
        self.order_id = order_id
        self.user_id = user_id
        self.items = []  # OrderItem 实体列表
        self.status = "pending"
        self.total_amount = 0

    def add_item(self, product_id, quantity, price):
        """添加订单项 - 通过聚合根操作"""
        item = OrderItem(product_id, quantity, price)
        self.items.append(item)
        self.total_amount += price * quantity

    def pay(self, payment_id):
        """支付订单"""
        if self.status != "pending":
            raise ValueError("订单状态不允许支付")
        self.status = "paid"
        # 发布领域事件
        return OrderPaidEvent(self.order_id, payment_id)

class OrderItem:
    """订单项实体"""
    def __init__(self, product_id, quantity, price):
        self.product_id = product_id
        self.quantity = quantity
        self.price = price

2.3 服务拆分策略

按业务能力拆分

# 电商系统按业务能力拆分
services:
  user-service:
    capability: 用户管理
    responsibilities:
      - 用户注册/登录
      - 用户信息管理
      - 地址管理

  product-service:
    capability: 商品管理
    responsibilities:
      - 商品CRUD
      - 分类管理
      - 库存管理

  order-service:
    capability: 订单管理
    responsibilities:
      - 订单创建
      - 订单状态管理
      - 订单查询

  payment-service:
    capability: 支付管理
    responsibilities:
      - 支付处理
      - 退款处理
      - 支付记录

按子域拆分

核心域(Core Domain)- 业务核心竞争力
├── 订单服务 - 订单流程是电商核心
└── 推荐服务 - 个性化推荐提升转化

支撑域(Supporting Domain)- 业务必需但非核心
├── 用户服务 - 用户管理
├── 商品服务 - 商品管理
└── 支付服务 - 支付处理

通用域(Generic Domain)- 通用功能
├── 通知服务 - 消息通知
├── 搜索服务 - 商品搜索
└── 文件服务 - 文件存储

按数据模型拆分

# 分析数据关系,识别服务边界

# 用户相关数据 → 用户服务
user_tables = ["users", "user_profiles", "addresses", "user_preferences"]

# 商品相关数据 → 商品服务
product_tables = ["products", "categories", "product_images", "inventory"]

# 订单相关数据 → 订单服务
order_tables = ["orders", "order_items", "order_status_history"]

# 跨服务数据访问通过 API,而非直接数据库查询

2.4 服务拆分实践案例

案例:电商系统拆分

初始单体架构

电商单体应用
├── 用户模块
├── 商品模块
├── 订单模块
├── 支付模块
├── 物流模块
└── 营销模块
    单一数据库

拆分步骤

第一步:识别服务边界

# 通过事件风暴识别边界
domain_events = {
    "用户域": ["用户注册", "用户登录", "地址变更"],
    "商品域": ["商品上架", "库存变更", "价格调整"],
    "订单域": ["订单创建", "订单支付", "订单发货", "订单完成"],
    "支付域": ["支付发起", "支付成功", "退款完成"],
}

第二步:定义服务接口

# 用户服务接口
class UserServiceInterface:
    def get_user(self, user_id: str) -> User:
        pass

    def create_user(self, user_data: dict) -> User:
        pass

    def update_address(self, user_id: str, address: Address) -> bool:
        pass

# 订单服务接口
class OrderServiceInterface:
    def create_order(self, user_id: str, items: list) -> Order:
        pass

    def get_order(self, order_id: str) -> Order:
        pass

    def pay_order(self, order_id: str, payment_info: dict) -> bool:
        pass

第三步:数据拆分

-- 用户服务数据库
CREATE DATABASE user_db;
-- 用户相关表迁移
CREATE TABLE users (...);
CREATE TABLE addresses (...);

-- 订单服务数据库
CREATE DATABASE order_db;
-- 订单相关表迁移
CREATE TABLE orders (...);
CREATE TABLE order_items (...);

第四步:服务间通信

# 订单服务调用用户服务
class OrderService:
    def __init__(self, user_client: UserServiceClient):
        self.user_client = user_client

    async def create_order(self, user_id: str, items: list):
        # 通过 API 获取用户信息
        user = await self.user_client.get_user(user_id)

        # 验证用户地址
        if not user.default_address:
            raise ValueError("用户没有默认地址")

        # 创建订单
        order = Order(user_id=user_id, items=items)
        return order

2.5 服务粒度控制

粒度判断标准

服务粒度评估维度:

1. 业务边界
   - 是否代表完整的业务能力?
   - 是否有独立的业务价值?

2. 数据边界
   - 是否有独立的数据模型?
   - 数据变更频率是否相似?

3. 团队边界
   - 是否可以由一个小团队独立维护?
   - 是否有清晰的负责人?

4. 变更频率
   - 功能变更是否相对独立?
   - 发布周期是否一致?

5. 扩展需求
   - 是否有独立的扩展需求?
   - 资源需求是否不同?

粒度过粗的问题

问题:用户服务包含太多职责
┌─────────────────────────────────────┐
│            用户服务                   │
│  ┌────────┐ ┌────────┐ ┌────────┐  │
│  │用户管理│ │认证授权│ │消息通知│  │
│  └────────┘ └────────┘ └────────┘  │
│  ┌────────┐ ┌────────┐             │
│  │积分管理│ │会员等级│             │
│  └────────┘ └────────┘             │
└─────────────────────────────────────┘

问题:
- 代码量大,难以维护
- 团队协作冲突
- 无法独立扩展
- 故障影响范围大

粒度过细的问题

问题:用户功能拆分过细
┌──────────┐ ┌──────────┐ ┌──────────┐
│用户创建服务│ │用户查询服务│ │用户更新服务│
└──────────┘ └──────────┘ └──────────┘
┌──────────┐ ┌──────────┐
│用户删除服务│ │用户验证服务│
└──────────┘ └──────────┘

问题:
- 服务数量爆炸
- 运维成本高
- 服务间调用复杂
- 分布式事务多

合适的粒度

合适的用户相关服务拆分:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│  用户服务    │ │  认证服务    │ │  通知服务    │
│  用户CRUD   │ │  登录/注册   │ │  站内信     │
│  地址管理   │ │  Token管理  │ │  邮件通知   │
│  会员等级   │ │  权限验证   │ │  短信通知   │
└─────────────┘ └─────────────┘ └─────────────┘

2.6 服务拆分反模式

1. 按技术层拆分

❌ 错误示例:按技术层拆分
┌─────────────┐
│  前端服务    │  所有前端代码
├─────────────┤
│  后端服务    │  所有后端代码
├─────────────┤
│  数据库服务  │  所有数据访问
└─────────────┘

问题:
- 业务逻辑分散
- 修改需要跨服务
- 无法独立部署

2. 按数据库表拆分

❌ 错误示例:一个表一个服务
┌──────────┐ ┌──────────┐ ┌──────────┐
│用户表服务 │ │订单表服务 │ │商品表服务 │
└──────────┘ └──────────┘ └──────────┘

问题:
- 业务逻辑被割裂
- 服务间耦合严重
- 事务处理复杂

3. 分布式单体

❌ 分布式单体:服务拆分但紧密耦合
┌─────────────┐     ┌─────────────┐
│  服务 A     │────→│  服务 B     │
│  (必须同步) │     │  (必须同步) │
└─────────────┘     └─────────────┘
       ↓                   ↓
   共享数据库,强依赖调用

问题:
- 失去微服务优势
- 部署困难
- 故障连锁反应

2.7 服务拆分检查清单

在拆分服务前,检查以下问题:

## 服务拆分检查清单

### 业务维度
- [ ] 服务是否代表完整的业务能力?
- [ ] 服务边界是否清晰?
- [ ] 服务是否有独立的业务价值?

### 技术维度
- [ ] 服务是否有独立的数据模型?
- [ ] 服务间接口是否清晰定义?
- [ ] 服务是否可以独立部署?

### 团队维度
- [ ] 是否有团队负责该服务?
- [ ] 团队是否有能力维护该服务?
- [ ] 是否会影响其他团队的开发?

### 运维维度
- [ ] 监控和告警是否就绪?
- [ ] 日志和追踪是否配置?
- [ ] 回滚方案是否准备?

### 成本维度
- [ ] 基础设施成本是否可接受?
- [ ] 运维成本是否可接受?
- [ ] 是否值得拆分?

2.8 小结

本章介绍了服务拆分的核心策略:

  1. 使用 DDD 的限界上下文识别服务边界
  2. 按业务能力或子域进行拆分
  3. 控制服务粒度,避免过粗或过细
  4. 避免按技术层或数据库表拆分的反模式
  5. 使用检查清单验证拆分决策

思考题

  1. 分析你当前系统,识别出可能的限界上下文
  2. 评估现有服务的粒度是否合适
  3. 是否存在分布式单体的问题?

参考资料