第二章:服务拆分策略与边界划分¶
2.1 服务拆分的核心原则¶
服务拆分是微服务架构设计中最关键的决策之一。拆分得当,系统灵活可维护;拆分不当,系统复杂混乱。
拆分原则¶
- 单一职责:每个服务只负责一个业务能力
- 高内聚低耦合:服务内部紧密关联,服务间松散耦合
- 业务优先:按业务边界拆分,而非技术边界
- 数据独立:每个服务管理自己的数据
- 团队对齐:服务边界与团队组织对齐
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 小结¶
本章介绍了服务拆分的核心策略:
- 使用 DDD 的限界上下文识别服务边界
- 按业务能力或子域进行拆分
- 控制服务粒度,避免过粗或过细
- 避免按技术层或数据库表拆分的反模式
- 使用检查清单验证拆分决策
思考题¶
- 分析你当前系统,识别出可能的限界上下文
- 评估现有服务的粒度是否合适
- 是否存在分布式单体的问题?