基于DDD领域驱动设计的淘客返利系统核心模型构建实践

Python3.8

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

基于DDD领域驱动设计的淘客返利系统核心模型构建实践

又见面了,我是高佣返利省赚客APP研发者微赚!

在复杂的电商返利业务中,传统的贫血模型往往导致业务逻辑散落在Service层,代码难以维护且极易产生Bug。为了解决这一痛点,省赚客APP的核心重构引入了领域驱动设计(DDD),通过限界上下文划分、聚合根设计与领域事件驱动,构建了高内聚、低耦合的核心模型。

限界上下文的战略划分

首先,我们将庞大的单体系统拆分为多个限界上下文(Bounded Context)。在返利场景中,主要划分为“订单上下文”、“佣金上下文”和“用户资产上下文”。每个上下文拥有独立的领域模型和数据库Schema,通过防腐层(ACL)进行交互,避免模型污染。

package juwatech.cn.provinceearn.domain.order.context;

import juwatech.cn.provinceearn.domain.order.entity.RebateOrder;
import juwatech.cn.provinceearn.domain.order.valueobject.OrderStatus;
import juwatech.cn.provinceearn.domain.order.repository.OrderRepository;
import juwatech.cn.provinceearn.domain.commission.event.OrderSettledEvent;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * 订单上下文中的聚合根服务
 */
@Component
public class OrderContextService {

    private final OrderRepository orderRepository;

    public OrderContextService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional
    public void confirmOrderReceipt(String orderId) {
        RebateOrder order = orderRepository.findById(orderId)
                .orElseThrow(() -> new IllegalArgumentException("Order not found"));
        
        // 领域行为:确认收货,状态流转内部完成
        order.confirmReceipt(); 
        
        if (order.getStatus() == OrderStatus.SETTLED) {
            // 发布领域事件,解耦佣金计算逻辑
            order.registerEvent(new OrderSettledEvent(
                order.getId(), 
                order.getUserId(), 
                order.getActualAmount(), 
                LocalDateTime.now()
            ));
            orderRepository.save(order);
        }
    }
}

富血模型的聚合根设计

在“佣金上下文”中,我们摒弃了简单的Getter/Setter,将业务规则封装在聚合根内部。佣金计算涉及复杂的费率阶梯、活动叠加和风控规则,这些逻辑必须由CommissionAggregate自行守护不变性。

package juwatech.cn.provinceearn.domain.commission.aggregate;

import juwatech.cn.provinceearn.domain.commission.valueobject.CommissionRate;
import juwatech.cn.provinceearn.domain.commission.valueobject.Money;
import juwatech.cn.provinceearn.domain.user.entity.UserLevel;
import juwatech.cn.provinceearn.domain.common.exception.BusinessRuleException;

import java.math.BigDecimal;

/**
 * 佣金聚合根
 */
public class CommissionAggregate {

    private String id;
    private String orderId;
    private Money estimatedAmount;
    private boolean isCalculated;

    public CommissionAggregate(String orderId, Money orderAmount, UserLevel userLevel) {
        this.orderId = orderId;
        this.isCalculated = false;
        // 领域逻辑:根据用户等级和订单金额动态计算预估佣金
        this.calculateInternal(orderAmount, userLevel);
    }

    private void calculateInternal(Money orderAmount, UserLevel userLevel) {
        // 封装复杂的风控与费率计算逻辑
        if (orderAmount.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessRuleException("Invalid order amount");
        }
        
        CommissionRate rate = CommissionRateFactory.getRate(userLevel, orderAmount);
        BigDecimal commission = orderAmount.getAmount().multiply(rate.getRate());
        
        // 确保佣金不低于最小阈值
        if (commission.compareTo(rate.getMinThreshold()) < 0) {
            commission = rate.getMinThreshold();
        }
        
        this.estimatedAmount = new Money(commission);
        this.isCalculated = true;
    }

    public Money getEstimatedAmount() {
        if (!isCalculated) {
            throw new IllegalStateException("Commission not calculated yet");
        }
        return this.estimatedAmount;
    }

    public void freeze() {
        // 冻结佣金,防止重复计算
        this.isCalculated = false; 
    }
}

领域事件驱动的最终一致性

跨上下文的协作不再依赖同步RPC调用,而是通过领域事件实现最终一致性。当订单状态变更时,发布OrderSettledEvent,佣金上下文监听该事件并触发佣金入账,用户资产上下文监听佣金入账事件更新余额。

package juwatech.cn.provinceearn.domain.commission.handler;

import juwatech.cn.provinceearn.domain.commission.aggregate.CommissionAggregate;
import juwatech.cn.provinceearn.domain.commission.repository.CommissionRepository;
import juwatech.cn.provinceearn.domain.order.event.OrderSettledEvent;
import juwatech.cn.provinceearn.domain.user.service.UserLevelService;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
public class CommissionEventHandler {

    private final CommissionRepository commissionRepository;
    private final UserLevelService userLevelService;

    public CommissionEventHandler(CommissionRepository commissionRepository, UserLevelService userLevelService) {
        this.commissionRepository = commissionRepository;
        this.userLevelService = userLevelService;
    }

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleOrderSettled(OrderSettledEvent event) {
        // 异步或同步处理取决于配置,此处演示核心逻辑
        var userLevel = userLevelService.getCurrentLevel(event.getUserId());
        
        // 构建新的佣金聚合
        CommissionAggregate newCommission = new CommissionAggregate(
            event.getOrderId(),
            event.getOrderAmount(),
            userLevel
        );
        
        // 持久化聚合
        commissionRepository.save(newCommission);
        
        // 此处可继续发布 CommissionCreatedEvent 触发后续流程
    }
}

值对象的不可变性保障

在DDD中,值对象(Value Object)至关重要。我们定义了严格的MoneyCommissionRate值对象,确保所有涉及金额的计算都是不可变的,避免了多线程环境下的数据竞争。

package juwatech.cn.provinceearn.domain.common.valueobject;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 金额值对象,保证不可变性和精度
 */
public final class Money {
    private final BigDecimal amount;

    public Money(BigDecimal amount) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Amount must be non-negative");
        }
        this.amount = amount.setScale(2, RoundingMode.HALF_UP);
    }

    public Money add(Money other) {
        return new Money(this.amount.add(other.amount));
    }

    public BigDecimal getAmount() {
        return amount;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Money)) return false;
        Money money = (Money) o;
        return amount.compareTo(money.amount) == 0;
    }

    @Override
    public int hashCode() {
        return amount.hashCode();
    }
}

通过上述实践,省赚客APP成功将复杂的业务逻辑收敛于领域模型之中,代码可读性与可测试性显著提升。面对频繁变动的返利规则,我们只需修改相应的聚合根或策略类,无需牵一发而动全身。这种架构不仅支撑了当前的亿级流量,更为未来的业务扩展奠定了坚实基础。

本文著作权归 省赚客app 研发团队,转载请注明出处!

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值