前言:为什么业务理解比技术本身更重要?
很多程序员工作3-5年后会遇到一个瓶颈:技术上能解决大部分问题,但总觉得做出来的东西“差一点”。差的这一点,往往不是代码写得不够优雅,而是对业务的理解不够深入。
在实际工作中,技术只是实现手段,业务的深度理解与建模能力才是区分“码农”和“工程师”的关键分水岭。一个工程师的价值,不在于他写了多少行代码,而在于他用代码解决了多少真实的业务问题。
本文将深入探讨业务建模的核心能力,从思维方法到实践技巧,从理论框架到代码落地,全方位解析如何提升这一关键能力。
一、什么是业务建模?
1.1 定义
业务建模是将现实世界中的业务需求、流程、规则、实体及它们之间的关系,通过抽象、归纳、演绎等思维方式,转化为计算机世界可理解、可执行的结构化模型的过程。
简单说:把“人怎么做事”翻译成“程序怎么做事”。
1.2 业务建模的四个层次
层次一:业务理解层 —— 听懂业务方在说什么
层次二:概念抽象层 —— 找出核心概念和关系
层次三:逻辑建模层 —— 定义规则、流程、约束
层次四:技术实现层 —— 用代码表达模型
大部分程序员停留在第四层(别人告诉你做什么你就实现什么),而优秀的工程师应该在前三层同样投入精力。
二、业务深度理解的核心技能
2.1 领域驱动设计(DDD)基础概念
领域驱动设计是业务建模的经典方法论,以下几个概念必须掌握:
领域(Domain):业务所覆盖的问题空间。比如电商系统的领域包括商品、订单、库存、支付等。
子域(Subdomain):将大领域拆分成小领域。
核心子域:业务核心竞争力所在(如推荐算法)
支撑子域:支持核心业务但非核心(如用户管理)
通用子域:通用解决方案(如支付接口)
限界上下文(Bounded Context):领域模型的边界,同一个概念在不同上下文中含义不同。
示例:在“商品上下文”中,“商品”关注属性、价格、库存;在“订单上下文”中,“商品”关注订单中的快照信息(价格、名称以订单时间为准)。
实体(Entity):有唯一标识,可变的对象。如用户(有userId)、订单(有orderId)。
值对象(Value Object):无唯一标识,不可变,通过属性值来判断相等性。如地址、金额、颜色。
聚合(Aggregate):一组相关对象的集合,有一个根实体(聚合根),外部只能通过聚合根访问内部对象。
领域事件(Domain Event):领域中发生的业务事件,如“订单已支付”“库存已扣减”。
2.2 事件风暴(Event Storming)
事件风暴是一种快速发现领域模型的工作坊方法,步骤:
识别领域事件:业务过程中发生了什么?用橙色便签贴在时间线上
识别命令:什么动作触发了事件?用蓝色便签
识别聚合:命令作用于什么对象?用黄色便签
识别外部系统:用户或外部服务?用粉色便签
示例:以“用户下单”流程为例
领域事件(橙色):订单已创建 → 库存已预扣 → 支付已完成 → 订单已确认
命令(蓝色):提交订单 → 扣减库存 → 完成支付 → 确认订单
聚合(黄色):订单聚合 → 库存聚合 → 支付聚合
外部系统(粉色):用户 → 支付网关
2.3 通用语言(Ubiquitous Language)
业务方和开发团队使用同一套词汇,避免理解偏差。
反面案例:
业务说“订单状态为待审核”
开发说“order_status = 1”
测试说“状态一”
正确做法:
统一使用“待审核”“已审核”“已发货”等业务词汇
代码中的类名、方法名、变量名都使用这些词汇
三、从业务到模型的转化方法
3.1 五步建模法
第一步:找出名词(实体和值对象)
取一份需求文档,圈出所有名词。
示例需求:
用户可以将商品加入购物车,购物车中的商品可以修改数量。下单时,系统根据商品总价和用户等级计算优惠,生成订单。订单包含收货地址、商品列表、总金额。
名词列表:用户、商品、购物车、数量、系统、总价、用户等级、优惠、订单、收货地址、商品列表、总金额
第二步:找出动词(行为和方法)
名词对应的动作:加入、修改、计算、生成
第三步:建立关系
用户 → 拥有 → 购物车
购物车 → 包含 → 商品(带数量)
商品 → 有 → 价格
订单 → 包含 → 订单商品(快照)
第四步:定义边界
哪些属于购物车上下文?哪些属于订单上下文?
购物车上下文:临时的、可随时修改
订单上下文:永久的、不可修改(快照)
第五步:用代码表达
# 商品值对象(在购物车上下文中)
@dataclass(frozen=True)
class Product:
product_id: str
name: str
price: Decimal
def apply_discount(self, percent: Decimal) -> Decimal:
return self.price * (Decimal('1') - percent / Decimal('100'))
# 购物车项值对象
@dataclass(frozen=True)
class CartItem:
product: Product
quantity: int
def subtotal(self) -> Decimal:
return self.product.price * self.quantity
def increase_quantity(self, delta: int) -> 'CartItem':
return CartItem(self.product, self.quantity + delta)
def decrease_quantity(self, delta: int) -> 'CartItem':
new_qty = max(0, self.quantity - delta)
return CartItem(self.product, new_qty)
# 购物车聚合(聚合根)
class ShoppingCart:
def __init__(self, user_id: str):
self.user_id = user_id
self._items: Dict[str, CartItem] = {}
self._created_at = datetime.now()
def add_item(self, product: Product, quantity: int):
if product.product_id in self._items:
current = self._items[product.product_id]
self._items[product.product_id] = current.increase_quantity(quantity)
else:
self._items[product.product_id] = CartItem(product, quantity)
def remove_item(self, product_id: str):
self._items.pop(product_id, None)
def update_quantity(self, product_id: str, new_quantity: int):
if product_id in self._items:
item = self._items[product_id]
self._items[product_id] = CartItem(item.product, new_quantity)
@property
def total(self) -> Decimal:
return sum(item.subtotal() for item in self._items.values())
@property
def items(self) -> List[CartItem]:
return list(self._items.values())
def checkout(self, user_level: str) -> Order:
"""结账:从购物车生成订单"""
if not self._items:
raise EmptyCartError("购物车为空")
# 计算优惠
discount = self._calculate_discount(user_level, self.total)
# 创建订单(订单上下文)
order = Order.create_from_cart(
user_id=self.user_id,
cart_items=self.items,
discount=discount,
original_total=self.total
)
# 清空购物车
self._items.clear()
return order
def _calculate_discount(self, user_level: str, total: Decimal) -> Decimal:
discount_rates = {
'gold': Decimal('0.15'),
'silver': Decimal('0.10'),
'bronze': Decimal('0.05'),
'normal': Decimal('0')
}
rate = discount_rates.get(user_level, Decimal('0'))
return total * rate
# 订单上下文:订单聚合
class Order:
def __init__(self, order_id: str, user_id: str):
self.order_id = order_id
self.user_id = user_id
self._items: List[OrderItem] = []
self._status = OrderStatus.PENDING
self._created_at = datetime.now()
self._shipping_address: Optional[Address] = None
self._original_total = Decimal('0')
self._discount = Decimal('0')
self._final_total = Decimal('0')
@classmethod
def create_from_cart(cls, user_id: str, cart_items: List[CartItem],
discount: Decimal, original_total: Decimal):
order = cls(str(uuid.uuid4()), user_id)
order._original_total = original_total
order._discount = discount
order._final_total = original_total - discount
# 创建订单商品快照(重要:复制而非引用)
for cart_item in cart_items:
order_item = OrderItem(
product_id=cart_item.product.product_id,
product_name=cart_item.product.name, # 快照名称
price_at_order=cart_item.product.price, # 快照价格
quantity=cart_item.quantity
)
order._items.append(order_item)
# 发布领域事件
order.add_domain_event(OrderCreatedEvent(order.order_id, user_id, order._final_total))
return order
def set_shipping_address(self, address: Address):
self._shipping_address = address
def pay(self, payment_result: PaymentResult):
if self._status != OrderStatus.PENDING:
raise InvalidOrderStateError("只有待支付的订单可以支付")
if payment_result.success:
self._status = OrderStatus.PAID
self.add_domain_event(OrderPaidEvent(self.order_id, payment_result.transaction_id))
else:
self._status = OrderStatus.PAYMENT_FAILED
def ship(self, tracking_number: str):
if self._status != OrderStatus.PAID:
raise InvalidOrderStateError("只有已支付的订单可以发货")
self._status = OrderStatus.SHIPPED
self._tracking_number = tracking_number
self.add_domain_event(OrderShippedEvent(self.order_id, tracking_number))
def add_domain_event(self, event: DomainEvent):
if not hasattr(self, '_domain_events'):
self._domain_events = []
self._domain_events.append(event)
@property
def domain_events(self):
return getattr(self, '_domain_events', [])
class OrderItem:
"""订单商品值对象(快照)"""
def __init__(self, product_id: str, product_name: str, price_at_order: Decimal, quantity: int):
self.product_id = product_id
self.product_name = product_name
self.price_at_order = price_at_order
self.quantity = quantity
def subtotal(self) -> Decimal:
return self.price_at_order * self.quantity
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
PAYMENT_FAILED = "payment_failed"
# 地址值对象
@dataclass(frozen=True)
class Address:
province: str
city: str
district: str
detail: str
postal_code: str
def full_address(self) -> str:
return f"{self.province}{self.city}{self.district}{self.detail}"
# 领域事件基类
@dataclass
class DomainEvent:
event_id: str
occurred_at: datetime
def __init__(self):
self.event_id = str(uuid.uuid4())
self.occurred_at = datetime.now()
@dataclass
class OrderCreatedEvent(DomainEvent):
order_id: str
user_id: str
total_amount: Decimal
def __init__(self, order_id: str, user_id: str, total_amount: Decimal):
super().__init__()
self.order_id = order_id
self.user_id = user_id
self.total_amount = total_amount
@dataclass
class OrderPaidEvent(DomainEvent):
order_id: str
transaction_id: str
def __init__(self, order_id: str, transaction_id: str):
super().__init__()
self.order_id = order_id
self.transaction_id = transaction_id
@dataclass
class OrderShippedEvent(DomainEvent):
order_id: str
tracking_number: str
def __init__(self, order_id: str, tracking_number: str):
super().__init__()
self.order_id = order_id
self.tracking_number = tracking_number
3.2 复杂业务规则的建模
业务往往包含大量规则,如何优雅地建模?
策略模式处理规则变化
from abc import ABC, abstractmethod
class DiscountStrategy(ABC):
@abstractmethod
def calculate(self, order: Order) -> Decimal:
pass
class NoDiscountStrategy(DiscountStrategy):
def calculate(self, order: Order) -> Decimal:
return Decimal('0')
class PercentageDiscountStrategy(DiscountStrategy):
def __init__(self, percentage: Decimal):
self.percentage = percentage
def calculate(self, order: Order) -> Decimal:
return order.subtotal * self.percentage / Decimal('100')
class TieredDiscountStrategy(DiscountStrategy):
"""阶梯折扣:满100减10,满200减25,满500减70"""
TIERS = [
(500, 70),
(200, 25),
(100, 10),
]
def calculate(self, order: Order) -> Decimal:
subtotal = order.subtotal
for threshold, discount in self.TIERS:
if subtotal >= threshold:
return Decimal(discount)
return Decimal('0')
class BirthdayDiscountStrategy(DiscountStrategy):
"""生日折扣:全场9折,但需要验证生日"""
def __init__(self, user_repository):
self.user_repository = user_repository
def calculate(self, order: Order) -> Decimal:
user = self.user_repository.find_by_id(order.user_id)
today = datetime.now().date()
if user.birthday.month == today.month and user.birthday.day == today.day:
return order.subtotal * Decimal('0.1') # 打9折
return Decimal('0')
# 组合折扣:支持多个优惠叠加
class CompositeDiscountStrategy(DiscountStrategy):
def __init__(self, strategies: List[DiscountStrategy]):
self.strategies = strategies
def calculate(self, order: Order) -> Decimal:
total_discount = Decimal('0')
for strategy in self.strategies:
discount = strategy.calculate(order)
total_discount += discount
# 折扣不能超过订单总额
return min(total_discount, order.subtotal)
# 使用示例
order = Order("order_123", "user_456")
order.add_item(Product("p1", "商品A", Decimal('299')), 1)
# 应用多重折扣
discount_strategy = CompositeDiscountStrategy([
TieredDiscountStrategy(),
BirthdayDiscountStrategy(user_repository)
])
discount = discount_strategy.calculate(order)
final_price = order.subtotal - discount
规格模式处理复杂判断条件
class Specification(ABC):
@abstractmethod
def is_satisfied_by(self, candidate: Any) -> bool:
pass
def and_(self, other: 'Specification') -> 'AndSpecification':
return AndSpecification(self, other)
def or_(self, other: 'Specification') -> 'OrSpecification':
return OrSpecification(self, other)
def not_(self) -> 'NotSpecification':
return NotSpecification(self)
class AndSpecification(Specification):
def __init__(self, left: Specification, right: Specification):
self.left = left
self.right = right
def is_satisfied_by(self, candidate: Any) -> bool:
return self.left.is_satisfied_by(candidate) and self.right.is_satisfied_by(candidate)
class OrSpecification(Specification):
def __init__(self, left: Specification, right: Specification):
self.left = left
self.right = right
def is_satisfied_by(self, candidate: Any) -> bool:
return self.left.is_satisfied_by(candidate) or self.right.is_satisfied_by(candidate)
class NotSpecification(Specification):
def __init__(self, spec: Specification):
self.spec = spec
def is_satisfied_by(self, candidate: Any) -> bool:
return not self.spec.is_satisfied_by(candidate)
# 具体规格实现
class OrderAmountGreaterThanSpecification(Specification):
def __init__(self, threshold: Decimal):
self.threshold = threshold
def is_satisfied_by(self, candidate: Order) -> bool:
return candidate.subtotal > self.threshold
class UserLevelSpecification(Specification):
def __init__(self, required_level: str):
self.required_level = required_level
def is_satisfied_by(self, candidate: Order) -> bool:
return candidate.user.level == self.required_level
class ItemCountSpecification(Specification):
def __init__(self, min_count: int = 1, max_count: int = 999):
self.min_count = min_count
self.max_count = max_count
def is_satisfied_by(self, candidate: Order) -> bool:
return self.min_count <= len(candidate.items) <= self.max_count
class NotContainsProductSpecification(Specification):
def __init__(self, product_id: str):
self.product_id = product_id
def is_satisfied_by(self, candidate: Order) -> bool:
return not any(item.product_id == self.product_id for item in candidate.items)
# 组合使用
class DiscountEligibilityChecker:
def __init__(self):
# VIP用户且订单金额大于200,且不包含特价商品
self.spec = (
UserLevelSpecification("vip")
.and_(OrderAmountGreaterThanSpecification(Decimal('200')))
.and_(NotContainsProductSpecification("special_item_001"))
)
def is_eligible(self, order: Order) -> bool:
return self.spec.is_satisfied_by(order)