2-1-1-所有痛苦来自边界不清

2.1.1 所有痛苦来自边界不清

引言

软件开发中的大部分问题不是技术问题,而是边界问题

  • "这个功能应该在前端还是后端处理?"
  • "这个逻辑属于用户模块还是订单模块?"
  • "这个配置应该在代码里还是数据库里?"

当边界不清时:

  • 代码职责混乱
  • 修改一处影响多处
  • 团队协作困难
  • Bug 无处不在

清晰的边界是软件架构的基石。

边界的本质

边界定义了"关注点"

# 边界不清:所有逻辑混在一起
@app.route('/api/orders', methods=['POST'])
def create_order():
    # HTTP 层逻辑
    data = request.get_json()

    # 验证逻辑
    if 'items' not in data:
        return {'error': 'Missing items'}, 400

    # 业务逻辑
    total = 0
    for item in data['items']:
        product = db.query(Product).get(item['id'])
        total += product.price * item['qty']

    # 数据持久化
    order = Order(user_id=data['user_id'], total=total)
    db.session.add(order)
    db.session.commit()

    # 外部服务调用
    send_email(data['user_id'], 'Order created')

    return {'order_id': order.id}, 201

问题:HTTP、验证、业务、数据库、外部服务全混在一起,边界模糊。

# 边界清晰:分层架构
# === HTTP 层(处理请求/响应)===
@app.route('/api/orders', methods=['POST'])
def create_order_endpoint():
    data = request.get_json()
    try:
        order = order_service.create_order(data)
        return {'order_id': order.id}, 201
    except ValidationError as e:
        return {'error': str(e)}, 400

# === 业务逻辑层(领域规则)===
class OrderService:
    def create_order(self, data):
        # 验证
        validate_order_data(data)

        # 业务逻辑
        total = calculate_order_total(data['items'])
        order = Order(user_id=data['user_id'], total=total)

        # 持久化
        order_repository.save(order)

        # 副作用
        notification_service.send_order_created(order)

        return order

# === 数据访问层(数据库操作)===
class OrderRepository:
    def save(self, order):
        db.session.add(order)
        db.session.commit()
        return order

# === 外部服务层(第三方集成)===
class NotificationService:
    def send_order_created(self, order):
        send_email(order.user_id, 'Order created')

好处:每一层有清晰的职责,修改某一层不影响其他层。

常见的边界混乱

混乱1:前端/后端边界不清

// 前端代码
function submitOrder(items) {
    // 错误:业务逻辑放在前端
    let total = 0;
    items.forEach(item => {
        total += item.price * item.quantity;

        // 折扣逻辑也在前端
        if (user.isVIP) {
            total *= 0.9;
        }
    });

    // 直接发送计算好的总价
    api.post('/orders', { items, total });
}

问题

  • 用户可以修改前端代码,改变 total
  • 业务逻辑重复(前后端都要写)
  • 前后端逻辑不一致时,出现 bug
// 正确:前端只负责收集数据和展示
function submitOrder(items) {
    // 前端只发送原始数据
    api.post('/orders', { items });
}

// 后端负责业务逻辑
@app.route('/orders', methods=['POST'])
def create_order():
    data = request.get_json()
    # 后端计算总价
    total = calculate_total(data['items'], current_user)
    # ...

混乱2:模块职责边界不清

# user.py
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    # 错误:订单逻辑混入用户模块
    def create_order(self, items):
        total = sum(item.price * item.qty for item in items)
        order = Order(user_id=self.id, total=total)
        db.save(order)
        return order

    # 错误:邮件发送逻辑也在用户模块
    def send_welcome_email(self):
        smtp.send(self.email, "Welcome", "...")

问题

  • User 类做太多事情
  • 修改订单逻辑要改 User 类
  • 违反单一职责原则
# 正确:清晰的模块边界

# user.py - 只负责用户相关
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    def is_vip(self):
        return self.membership_level == 'vip'

# order.py - 只负责订单相关
class OrderService:
    def create_order(self, user, items):
        total = self._calculate_total(items, user)
        order = Order(user_id=user.id, total=total)
        self.order_repo.save(order)
        return order

# notification.py - 只负责通知相关
class NotificationService:
    def send_welcome_email(self, user):
        smtp.send(user.email, "Welcome", "...")

混乱3:数据/逻辑边界不清

# 错误:逻辑和数据混在一起
orders_data = [
    {'id': 1, 'user_id': 100, 'total': 99.9},
    {'id': 2, 'user_id': 101, 'total': 150.0}
]

# 处理逻辑直接操作字典
for order_dict in orders_data:
    if order_dict['total'] > 100:
        order_dict['shipping'] = 'free'  # 修改原始数据
    else:
        order_dict['shipping'] = 10

# 问题:逻辑分散,数据结构暴露
# 正确:用对象封装数据和逻辑
class Order:
    def __init__(self, id, user_id, total):
        self.id = id
        self.user_id = user_id
        self.total = total

    def calculate_shipping(self):
        """业务逻辑封装在对象内"""
        return 0 if self.total > 100 else 10

# 使用
orders = [
    Order(1, 100, 99.9),
    Order(2, 101, 150.0)
]

for order in orders:
    shipping = order.calculate_shipping()

如何划分边界

原则1:按变化原因划分

不同的变化原因 = 不同的模块

# 订单相关的变化原因:
# - 订单业务规则变化(折扣、税费)
# - 订单数据模型变化
→ Order 模块

# 用户相关的变化原因:
# - 用户权限规则变化
# - 用户资料字段变化
→ User 模块

# 通知相关的变化原因:
# - 通知渠道变化(邮件、短信、推送)
# - 通知模板变化
→ Notification 模块

原则2:按职责划分

单一职责原则(SRP):一个模块只做一类事情。

# 好的划分
class UserAuthentication:  # 职责:认证
    def login(self, username, password): ...
    def logout(self, session): ...

class UserProfile:  # 职责:用户资料
    def get_profile(self, user_id): ...
    def update_profile(self, user_id, data): ...

class UserPermissions:  # 职责:权限管理
    def has_permission(self, user, permission): ...
    def grant_permission(self, user, permission): ...

原则3:按依赖方向划分

核心业务 ← 基础设施

# 错误:业务逻辑依赖具体实现
class OrderService:
    def create_order(self, data):
        # 直接依赖 MySQL
        mysql.execute("INSERT INTO orders ...")

# 正确:业务逻辑依赖抽象
class OrderService:
    def __init__(self, order_repository):
        # 依赖抽象接口
        self.repo = order_repository

    def create_order(self, data):
        order = Order(**data)
        self.repo.save(order)  # 不关心底层是 MySQL 还是 PostgreSQL

# 具体实现在外层
class MySQLOrderRepository:
    def save(self, order):
        mysql.execute("INSERT INTO orders ...")

边界的表现形式

形式1:包/模块

project/
├── domain/           # 核心领域逻辑
│   ├── user.py
│   └── order.py
├── application/      # 应用服务
│   ├── user_service.py
│   └── order_service.py
├── infrastructure/   # 基础设施
│   ├── database.py
│   └── email.py
└── interfaces/       # 接口层
    ├── api.py
    └── cli.py

形式2:接口/契约

from abc import ABC, abstractmethod

# 定义边界:抽象接口
class OrderRepository(ABC):
    @abstractmethod
    def save(self, order):
        pass

    @abstractmethod
    def get(self, order_id):
        pass

# 实现1:MySQL
class MySQLOrderRepository(OrderRepository):
    def save(self, order):
        # MySQL 具体实现
        ...

# 实现2:MongoDB
class MongoOrderRepository(OrderRepository):
    def save(self, order):
        # MongoDB 具体实现
        ...

形式3:API契约

# HTTP API 定义边界
# 前端只需要知道这个契约,不需要知道后端实现

"""
POST /api/orders
Request:
{
    "user_id": 123,
    "items": [
        {"product_id": 1, "quantity": 2}
    ]
}

Response:
{
    "order_id": 456,
    "total": 99.90,
    "status": "pending"
}
"""

边界混乱的症状

症状1:修改一处影响多处

# 症状:改订单逻辑,用户模块也要改
# 原因:边界不清,职责混乱

症状2:难以测试

# 症状:测试订单创建,必须启动整个系统
# 原因:边界不清,依赖太多

症状3:团队协作困难

# 症状:两个开发者同时修改同一个文件
# 原因:边界不清,职责集中在少数文件

重构边界的策略

策略:识别混乱点

# 问自己
1. 这个类/函数做了几件事?(超过1件 = 边界混乱)
2. 修改这个需求需要改几个地方?(超过1个模块 = 边界混乱)
3. 这个模块有几个修改的理由?(超过1个 = 边界混乱)

策略:渐进式重构

# 不要一次性大重构
# 每次只重构一个边界

# 步骤1:提取接口
class UserService:
    def create_order(self, items):
        # 先提取订单创建逻辑到独立函数
        return self._create_order_logic(items)

# 步骤2:独立模块
class OrderService:
    def create_order(self, user_id, items):
        # 移到独立模块
        ...

# 步骤3:修改调用方
class UserService:
    def __init__(self, order_service):
        self.order_service = order_service

    def create_order(self, items):
        return self.order_service.create_order(self.user_id, items)

总结

所有痛苦来自边界不清:

  1. 边界定义职责——每个模块只做一件事
  2. 清晰的边界让系统可维护——修改局部不影响全局
  3. 边界通过接口表达——依赖抽象而非具体
  4. 边界按变化原因划分——不同原因 = 不同模块
  5. 渐进式重构边界——不要一次性大改

记住:

当你不知道一段代码应该放在哪里时,说明边界不清。

好的架构让每个模块的职责一目了然。

花时间厘清边界,能节省未来数倍的维护时间。

posted @ 2025-11-29 21:55  Jack_Q  阅读(0)  评论(0)    收藏  举报