程序员进阶工程师必备的十大技能之业务深度理解与建模能力(二)

简介: 教程来源 https://unbgv.cn/ 本文系统阐述业务建模最佳实践:坚持单一职责、不变性保护与显式建模原则;警惕过度设计、贫血模型与技术驱动陷阱;强调持续精炼、事件追溯与业务对齐。以电商促销系统为例,通过抽象优惠券类型、使用条件与叠加规则,构建可扩展、易演进的领域模型。

四、业务建模的最佳实践

4.1 建模原则

  1. 单一职责原则(SRP)

一个聚合根只负责一个明确的核心业务。不要把订单的支付、库存、物流全部塞进订单聚合。

# 错误:订单聚合做了太多事
class BadOrder:
    def pay(self): pass
    def check_inventory(self): pass
    def calculate_shipping(self): pass
    def send_notification(self): pass

# 正确:分离关注点
class Order:
    def pay(self, payment_service): pass

class InventoryService:
    def reserve(self, order): pass

class ShippingService:
    def calculate_fee(self, order): pass

class NotificationService:
    def send_order_confirmation(self, order): pass

2.不变性保护

业务规则需要被严格保护,不要让外部直接修改核心状态。

class Order:
    def __init__(self):
        self._status = OrderStatus.PENDING
        self._items = []

    @property
    def status(self):
        return self._status

    @property
    def items(self):
        # 返回副本,防止外部直接修改
        return list(self._items)

    def add_item(self, product, quantity):
        # 业务规则:已支付的订单不能添加商品
        if self._status != OrderStatus.PENDING:
            raise OrderAlreadyPaidError("订单已支付,无法修改")

        # 业务规则:单个商品数量不能超过10
        if quantity > 10:
            raise QuantityLimitError("单个商品购买数量不能超过10")

        self._items.append(OrderItem(product, quantity))

3.显式建模隐式概念

很多业务规则是隐性的,需要显式地用代码表达。

# 隐式:直接用字符串
order.status = "paid"

# 显式:用枚举
class OrderStatus(Enum):
    PENDING = "pending"
    PAID = "paid"
    SHIPPED = "shipped"

# 更隐式:用数值比较
if amount > 100:
    discount = 10

# 更显式:用命名常量或规格
class DiscountRule:
    MIN_AMOUNT_FOR_DISCOUNT = Decimal('100')
    DISCOUNT_AMOUNT = Decimal('10')

if amount >= DiscountRule.MIN_AMOUNT_FOR_DISCOUNT:
    discount = DiscountRule.DISCOUNT_AMOUNT

# 或者用规格模式
class EligibleForDiscountSpec(Specification):
    def is_satisfied_by(self, order):
        return order.amount >= DiscountRule.MIN_AMOUNT_FOR_DISCOUNT

4.2 避免的陷阱
陷阱1:过度设计

不要一开始就做完美的模型。从最简单的模型开始,随着业务理解加深逐步演进。

陷阱2:贫血模型

只包含数据没有行为的模型。

# 贫血模型(坏)
class OrderData:
    def __init__(self):
        self.id = None
        self.status = None
        self.items = []

# 业务逻辑散落在Service层
class OrderService:
    def pay(self, order_data, payment_info):
        if order_data.status == "pending":
            # 调用支付接口
            order_data.status = "paid"

陷阱3:技术驱动而非业务驱动

为了用某个设计模式而用,而不是为了解决业务问题。

4.3 持续精炼模型
模型不是一次建成的,需要持续迭代:

与业务方定期沟通:验证模型是否符合预期

代码即文档:好的模型代码本身就是文档

重构不设限:发现模型缺陷及时重构

事件追溯:通过领域事件理解业务变迁

五、实战案例:电商促销系统建模

5.1 需求描述
设计一个促销系统,支持:

满减券:满100减10

折扣券:8折券,最高优惠50元

单品券:指定商品立减

叠加规则:部分券可叠加,部分不可

使用条件:订单金额、用户等级、商品范围等限制

5.2 建模过程
第一步:识别核心概念

优惠券(Coupon):包含类型、面额、使用条件

用户(User):领券、用券

订单(Order):使用优惠券

促销规则(PromotionRule):判断是否可用

优惠计算(DiscountCalculation):计算结果

第二步:建立模型

from abc import ABC, abstractmethod
from typing import Optional, List
from decimal import Decimal
from enum import Enum
from dataclasses import dataclass
from datetime import datetime

class CouponType(Enum):
    CASH_DEDUCTION = "cash_deduction"  # 满减券
    PERCENTAGE_DISCOUNT = "percentage_discount"  # 折扣券
    FIXED_PRICE = "fixed_price"  # 单品立减券

@dataclass
class UsageCondition:
    """使用条件值对象"""
    min_order_amount: Optional[Decimal] = None  # 最低订单金额
    applicable_user_levels: List[str] = None  # 适用用户等级
    applicable_product_ids: List[str] = None  # 适用商品ID
    applicable_categories: List[str] = None  # 适用商品分类
    exclude_product_ids: List[str] = None  # 排除商品ID

    def is_satisfied_by(self, order: 'Order', user: 'User') -> bool:
        # 检查订单金额
        if self.min_order_amount and order.subtotal < self.min_order_amount:
            return False

        # 检查用户等级
        if self.applicable_user_levels and user.level not in self.applicable_user_levels:
            return False

        # 检查商品范围
        if self.applicable_product_ids:
            order_product_ids = {item.product_id for item in order.items}
            if not order_product_ids.issubset(set(self.applicable_product_ids)):
                return False

        # 检查排除商品
        if self.exclude_product_ids:
            order_product_ids = {item.product_id for item in order.items}
            if order_product_ids.intersection(set(self.exclude_product_ids)):
                return False

        return True

class Coupon(ABC):
    """优惠券抽象基类"""

    def __init__(self, coupon_id: str, name: str, condition: UsageCondition):
        self.coupon_id = coupon_id
        self.name = name
        self.condition = condition
        self.created_at = datetime.now()
        self.used = False

    @abstractmethod
    def calculate_discount(self, order: 'Order', user: 'User') -> Decimal:
        """计算优惠金额"""
        pass

    def can_apply(self, order: 'Order', user: 'User') -> bool:
        """检查优惠券是否可用"""
        if self.used:
            return False
        return self.condition.is_satisfied_by(order, user)

class CashDeductionCoupon(Coupon):
    """满减券:满X减Y"""

    def __init__(self, coupon_id: str, name: str, condition: UsageCondition, 
                 threshold: Decimal, deduction: Decimal):
        super().__init__(coupon_id, name, condition)
        self.threshold = threshold  # 满多少
        self.deduction = deduction  # 减多少

        # 自动设置最低金额条件
        if not condition.min_order_amount or condition.min_order_amount < threshold:
            condition.min_order_amount = threshold

    def calculate_discount(self, order: 'Order', user: 'User') -> Decimal:
        if not self.can_apply(order, user):
            return Decimal('0')

        if order.subtotal >= self.threshold:
            return min(self.deduction, order.subtotal)
        return Decimal('0')

class PercentageDiscountCoupon(Coupon):
    """折扣券:打X折,最高优惠Y元"""

    def __init__(self, coupon_id: str, name: str, condition: UsageCondition,
                 discount_rate: Decimal, max_discount: Optional[Decimal] = None):
        super().__init__(coupon_id, name, condition)
        # discount_rate: 0.8 表示8折,0.9表示9折
        self.discount_rate = discount_rate
        self.max_discount = max_discount or Decimal('inf')

    def calculate_discount(self, order: 'Order', user: 'User') -> Decimal:
        if not self.can_apply(order, user):
            return Decimal('0')

        # 计算折扣金额
        discount = order.subtotal * (Decimal('1') - self.discount_rate)

        # 应用最高优惠限制
        final_discount = min(discount, self.max_discount)

        # 折扣不能超过订单金额
        return min(final_discount, order.subtotal)

class FixedPriceCoupon(Coupon):
    """单品立减券:指定商品减指定金额"""

    def __init__(self, coupon_id: str, name: str, condition: UsageCondition,
                 product_id: str, deduction: Decimal):
        super().__init__(coupon_id, name, condition)
        self.product_id = product_id
        self.deduction = deduction

        # 设置适用商品
        if not condition.applicable_product_ids:
            condition.applicable_product_ids = [product_id]

    def calculate_discount(self, order: 'Order', user: 'User') -> Decimal:
        if not self.can_apply(order, user):
            return Decimal('0')

        # 找到指定商品
        for item in order.items:
            if item.product_id == self.product_id:
                # 不能超过商品价格
                return min(self.deduction, item.price * item.quantity)
        return Decimal('0')

class StackingRule:
    """优惠券叠加规则"""

    def __init__(self):
        self.allowed_combinations = {}  # 类型 -> 可叠加的类型列表
        self._init_default_rules()

    def _init_default_rules(self):
        # 默认规则:同类型优惠券不能叠加
        for coupon_type in CouponType:
            self.allowed_combinations[coupon_type] = [coupon_type]

    def can_stack(self, coupon1: Coupon, coupon2: Coupon) -> bool:
        """判断两个优惠券能否叠加"""
        type1 = self._get_coupon_type(coupon1)
        type2 = self._get_coupon_type(coupon2)

        # 检查类型1是否允许类型2叠加
        if type2 not in self.allowed_combinations.get(type1, []):
            return False

        # 检查类型2是否允许类型1叠加
        if type1 not in self.allowed_combinations.get(type2, []):
            return False

        # 检查是否有互斥的商品
        return self._check_no_conflict(coupon1, coupon2)

    def _get_coupon_type(self, coupon: Coupon) -> CouponType:
        if isinstance(coupon, CashDeductionCoupon):
            return CouponType.CASH_DEDUCTION
        elif isinstance(coupon, PercentageDiscountCoupon):
            return CouponType.PERCENTAGE_DISCOUNT
        elif isinstance(coupon, FixedPriceCoupon):
            return CouponType.FIXED_PRICE
        raise ValueError("Unknown coupon type")

    def _check_no_conflict(self, coupon1: Coupon, coupon2: Coupon) -> bool:
        # 检查是否有商品同时被两个单品券锁定
        fixed_coupons = []
        for c in [coupon1, coupon2]:
            if isinstance(c, FixedPriceCoupon):
                fixed_coupons.append(c)

        if len(fixed_coupons) > 1 and fixed_coupons[0].product_id != fixed_coupons[1].product_id:
            # 两个不同的单品券作用于不同商品,可以叠加
            return True
        elif len(fixed_coupons) > 1:
            # 同一个商品用了两张单品券,取最优
            return False

        return True

class ShoppingCartWithPromotion:
    """支持促销的购物车"""

    def __init__(self, user: 'User', stacking_rule: StackingRule = None):
        self.user = user
        self._items = []
        self.selected_coupons = []
        self.stacking_rule = stacking_rule or StackingRule()

    def add_item(self, product, quantity: int):
        self._items.append(OrderItem(product, quantity))

    def select_coupons(self, coupons: List[Coupon]):
        """选择要使用的优惠券组合"""
        # 验证是否可以叠加
        for i in range(len(coupons)):
            for j in range(i + 1, len(coupons)):
                if not self.stacking_rule.can_stack(coupons[i], coupons[j]):
                    raise CouponStackError(f"优惠券 {coupons[i].name} 和 {coupons[j].name} 不能叠加使用")

        self.selected_coupons = coupons

    @property
    def items(self):
        return list(self._items)

    @property
    def subtotal(self) -> Decimal:
        return sum(item.subtotal() for item in self._items)

    def calculate_final_price(self) -> dict:
        """计算最终价格和优惠明细"""
        original_total = self.subtotal
        remaining_amount = original_total
        discount_details = []

        # 计算优惠券的优先级:单品券 > 满减券 > 折扣券
        sorted_coupons = sorted(self.selected_coupons, 
                               key=lambda c: self._coupon_priority(c))

        for coupon in sorted_coupons:
            if remaining_amount <= 0:
                break

            discount = coupon.calculate_discount(self._get_temp_order(remaining_amount), self.user)
            if discount > 0:
                discount_details.append({
                    'coupon_name': coupon.name,
                    'discount': discount
                })
                remaining_amount -= discount

        final_total = max(remaining_amount, Decimal('0'))

        return {
            'original_total': original_total,
            'total_discount': original_total - final_total,
            'final_total': final_total,
            'discount_details': discount_details
        }

    def _coupon_priority(self, coupon: Coupon) -> int:
        """优惠券优先级:数字越小优先级越高"""
        if isinstance(coupon, FixedPriceCoupon):
            return 1
        elif isinstance(coupon, CashDeductionCoupon):
            return 2
        elif isinstance(coupon, PercentageDiscountCoupon):
            return 3
        return 4

    def _get_temp_order(self, remaining_amount):
        """创建临时订单用于计算(模拟已有优惠后的订单)"""
        class TempOrder:
            def __init__(self, items, amount):
                self.items = items
                self.subtotal = amount

        return TempOrder(self.items, remaining_amount)

# 使用示例
def main():
    # 创建用户
    user = User("user_001", level="vip")

    # 创建商品
    laptop = Product("p001", "笔记本电脑", Decimal('5999'))
    mouse = Product("p002", "鼠标", Decimal('99'))

    # 创建购物车
    cart = ShoppingCartWithPromotion(user)
    cart.add_item(laptop, 1)
    cart.add_item(mouse, 2)

    print(f"原始总价:¥{cart.subtotal}")
    # 原始总价:¥6197 (5999 + 99*2)

    # 创建优惠券
    # 1. 满5000减500的满减券
    condition1 = UsageCondition(min_order_amount=Decimal('5000'))
    cash_coupon = CashDeductionCoupon(
        "c001", "618大促满减券", condition1,
        threshold=Decimal('5000'), deduction=Decimal('500')
    )

    # 2. 全场9折券,最高优惠300
    condition2 = UsageCondition()
    discount_coupon = PercentageDiscountCoupon(
        "c002", "VIP专享9折券", condition2,
        discount_rate=Decimal('0.9'), max_discount=Decimal('300')
    )

    # 3. 鼠标单品立减20元券
    condition3 = UsageCondition()
    fixed_coupon = FixedPriceCoupon(
        "c003", "鼠标优惠券", condition3,
        product_id="p002", deduction=Decimal('20')
    )

    # 测试不同组合

    # 组合1:只用满减券
    cart.select_coupons([cash_coupon])
    result = cart.calculate_final_price()
    print(f"只用满减券:¥{result['final_total']} (省¥{result['total_discount']})")

    # 组合2:满减+鼠标券
    cart.select_coupons([cash_coupon, fixed_coupon])
    result = cart.calculate_final_price()
    print(f"满减+鼠标券:¥{result['final_total']} (省¥{result['total_discount']})")
    for detail in result['discount_details']:
        print(f"  - {detail['coupon_name']}: 省¥{detail['discount']}")

    # 组合3:满减+折扣券(需要检查叠加规则)
    try:
        cart.select_coupons([cash_coupon, discount_coupon])
        result = cart.calculate_final_price()
        print(f"满减+折扣券:¥{result['final_total']} (省¥{result['total_discount']})")
    except CouponStackError as e:
        print(f"叠加失败:{e}")

第三步:模型演进

随着业务发展,促销系统需要支持:

秒杀价

拼团优惠

会员日额外折扣

模型通过扩展而非修改的方式来应对变化(开放封闭原则)。
来源:
https://htnus.cn/

相关文章
|
2月前
|
设计模式 监控 程序员
程序员必备的十大技能(进阶版)之架构规划与项目统筹(二)
教程来源 http://oieaw.cn/ 本文系统阐述微服务架构设计核心:基于限界上下文划分订单、库存、支付等清晰边界;通过防腐层隔离外部依赖(如物流系统);遵循单一职责、数据自治等服务划分原则;并全面覆盖性能、可用性、安全等非功能性需求,集成SLI/SLO/SLA监控及超时、重试、熔断、舱壁等容错机制。
|
2月前
|
JSON 监控 程序员
程序员进阶工程师必备技能之架构落地与组件封装(二)
教程来源 https://tmywi.cn/ 本文系统讲解组件封装与架构落地的实战方法:涵盖组件定义原则(可重用、可组合、封装性等五大特征)、三大通用组件封装——配置管理(多源合并/热更新)、结构化日志(JSON/彩色控制台/上下文追踪)、异步数据库连接池(健康检查/慢查询监控/事务支持);并延伸至语义化版本发布、ADR决策记录、架构度量与渐进式演进策略,助力工程化落地。
|
机器学习/深度学习 前端开发 数据可视化
神奇的streamlit (哇 原来深度学习还可以这样玩)
神奇的streamlit (哇 原来深度学习还可以这样玩)
神奇的streamlit (哇 原来深度学习还可以这样玩)
|
2月前
|
存储 人工智能 运维
千亿级 AI 搜索的效能实战:从混合检索到 Agentic RAG 的三年实战
本文为2026 Elastic中国大会演讲实录,直击千亿级AI搜索三大挑战:搜索融合(关键词+向量+稀疏检索原生一体)、极致效能(冷热分层、硬件降级、自研FalconSeek引擎)与Agentic RAG演进(结构化知识图谱+智能体自主推理),揭示企业级AI搜索从“能用”到“好用”再到“自进化”的实战路径。
683 8
|
2月前
|
缓存 前端开发 安全
ReAct推理链的工程化实现与最佳实践
本文介绍向量空间JBoltAI平台基于Spring Boot 3.x与Java 21实现的企业级ReAct推理链架构,涵盖分层设计、模板方法、Function Calling驱动、并发安全机制及推理可视化等核心实践,助力LLM能力可靠落地。
|
2月前
|
人工智能 安全 算法
GEO 行业大清洗:倒闭的不是公司,是整个行业的投机小聪明
本文深度剖析GEO行业2026年集体暴雷的根源:AI技术迭代彻底颠覆旧有商业逻辑。指出虚假繁荣源于信息差红利,而今算法升级、监管加码与品牌认知觉醒共同终结“铺量套利”模式。文章穿透表象,从商业本质、技术底层、人性痛点、产业终局四维拆解,宣告低端中介退场,真GEO已升级为以AI认知基建、品牌信用沉淀为核心的高价值赛道。(239字)
|
2月前
|
SQL 关系型数据库 MySQL
SQL代码审查指南:命名规范+10大反模式+四维检查清单,一篇全搞定
数据库小学妹带你攻克SQL规范难题!从命名、格式到10大反模式(如SELECT*、隐式转换、ORDER BY RAND等),结合真实踩坑案例,详解可读、可维护、高性能的SQL写法,并提供SQL Review四维审查清单与团队落地方法,助你写出工业级质量SQL。
|
2月前
|
机器学习/深度学习 自然语言处理 运维
从零搞懂大模型:定义、起源、计量单位与完整分类|入门必看干货
本文用通俗语言系统梳理大模型核心知识:明确定义(参数≥10亿)、爆发根源(数据+算力+Transformer三要素)、三大计量单位(B/Token/FLOPS)、分类体系(模态/功能)及开源vs闭源逻辑,助新手建立扎实认知基础,为后续微调、RAG、智能体开发铺路。(239字)
863 2
|
2月前
|
人工智能 调度 流计算
Why Will OPC One-Person Companies Emerge in the AI Era? Understanding the New Individual Business Model Driven by AI Agents
AI时代一人公司(OPC)兴起,源于大模型、AI智能体与自动化工具对个体能力的倍增效应:单人即可调度AI完成研发、营销、交付等全链路闭环,实现“一人成军”。
298 2
|
2月前
|
机器学习/深度学习 人工智能 弹性计算
阿里云服务器经济型e实例2核2G配置99元怎么样?具体性能、购买资格与适用场景解析
阿里云ECS经济型e实例(2核2G/3M带宽/40G ESSD云盘)以99元/年特惠价面向个人及企业用户开放,活动截止2027年3月31日,新老同享且续费同价。该实例基于Intel至强可扩展处理器,采用共享CPU架构,适合个人博客、开发测试、轻量网站等低负载场景,不适合高并发与深度学习等重负载任务。另有u1实例(2核4G/199元/年)供企业用户选择,提供100%算力保障。