六、业务建模的工程实践
6.1 建模文档化
用代码 + 注释 + 可视化图表来描述模型:
# 订单模型说明
## 核心概念
- Order(订单):一次交易的记录
- OrderItem(订单项):订单中的商品快照
- OrderStatus(订单状态):订单生命周期中的状态
## 状态机
[待支付] -> (支付) -> [已支付] -> (发货) -> [已发货] -> (确认收货) -> [已完成]
|
v
[支付失败] -> (重新支付) -> [待支付]
|
v
[已取消]
## 业务规则
1. 订单总金额 <= 0 时不能创建订单
2. 已支付的订单不能取消(需联系客服)
3. 订单商品数量不能超过库存
4. 订单价格以下单时刻为准(快照)
## 使用示例
\```python
order = Order.create(user_id, items)
order.pay(PaymentInfo(amount=order.total))
order.ship("SF1234567890")
\
**6.2 模型验证**
通过单元测试来验证业务规则:
import pytest
from decimal import Decimal
class TestOrderModel:
def test_order_creation_with_valid_data(self):
"""测试:使用有效数据创建订单"""
user_id = "user_001"
items = [OrderItem(Product("p1", "商品1", Decimal('100')), 2)]
order = Order.create(user_id, items)
assert order.user_id == user_id
assert order.status == OrderStatus.PENDING
assert order.subtotal == Decimal('200')
def test_order_creation_with_empty_items(self):
"""测试:空订单不能创建"""
with pytest.raises(EmptyOrderError):
Order.create("user_001", [])
def test_pay_increases_status_to_paid(self):
"""测试:支付成功后状态变为已支付"""
order = create_test_order()
order.pay(PaymentResult(success=True))
assert order.status == OrderStatus.PAID
def test_cannot_pay_twice(self):
"""测试:不能重复支付同一个订单"""
order = create_test_order()
order.pay(PaymentResult(success=True))
with pytest.raises(InvalidOrderStateError):
order.pay(PaymentResult(success=True))
def test_cannot_modify_paid_order(self):
"""测试:已支付的订单不能修改商品"""
order = create_test_order()
order.pay(PaymentResult(success=True))
with pytest.raises(OrderAlreadyPaidError):
order.add_item(Product("p2", "新商品", Decimal('50')), 1)
def test_domain_events_are_emitted(self):
"""测试:领域事件正确触发"""
order = create_test_order()
order.pay(PaymentResult(success=True))
events = order.domain_events
assert len(events) == 1
assert isinstance(events[0], OrderPaidEvent)
assert events[0].order_id == order.order_id
**6.3 与数据库的映射**
业务模型与持久化存储的映射策略:
使用 Repository 模式隔离模型与存储
class OrderRepository:
def init(self, db_session):
self.db_session = db_session
def save(self, order: Order):
"""保存订单聚合"""
# 保存订单主表
order_po = OrderPO(
order_id=order.order_id,
user_id=order.user_id,
status=order.status.value,
original_total=order.original_total,
final_total=order.final_total,
created_at=order.created_at
)
self.db_session.merge(order_po)
# 保存订单商品表
for item in order.items:
item_po = OrderItemPO(
order_id=order.order_id,
product_id=item.product_id,
product_name=item.product_name,
price_at_order=item.price_at_order,
quantity=item.quantity
)
self.db_session.merge(item_po)
# 保存领域事件(如果需要持久化)
for event in order.domain_events:
event_po = DomainEventPO(
event_id=event.event_id,
order_id=order.order_id,
event_type=type(event).__name__,
event_data=json.dumps(asdict(event)),
occurred_at=event.occurred_at
)
self.db_session.add(event_po)
def find_by_id(self, order_id: str) -> Optional[Order]:
"""根据ID查询订单"""
order_po = self.db_session.query(OrderPO).filter_by(order_id=order_id).first()
if not order_po:
return None
# 查询订单商品
item_pos = self.db_session.query(OrderItemPO).filter_by(order_id=order_id).all()
# 重建订单聚合
order = Order._reconstruct(
order_id=order_po.order_id,
user_id=order_po.user_id,
status=OrderStatus(order_po.status),
items=[OrderItem._reconstruct(
product_id=ip.product_id,
product_name=ip.product_name,
price_at_order=ip.price_at_order,
quantity=ip.quantity
) for ip in item_pos],
original_total=order_po.original_total,
final_total=order_po.final_total,
created_at=order_po.created_at
)
return order
```
总结
业务深度理解与建模能力是将程序员与工程师区分开的关键能力。它不仅要求我们写出能运行的代码,更要求我们写出能准确表达业务的代码。
核心要点回顾:
从业务出发:理解业务方真正想要什么
用模型思考:在写代码前先建立概念模型
代码即模型:让代码直接反映业务概念
保护业务规则:业务逻辑封装在模型中
持续演进:模型随着业务理解深入不断优化
记住:你不是在写代码,你是在用代码构建一个和业务世界平行的数字世界。这个世界越贴近真实业务,你的价值就越大。
来源:
https://oieaw.cn/