You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DDD实践答疑:新增需求时如何避免领域逻辑漏洞?

解决DDD中客户积分调整的领域逻辑漏洞问题

核心思路是明确区分两种积分扣除场景:用户主动兑换(需要250积分门槛)和订单修改导致的系统调整(无门槛但需保证积分不为负),同时通过封装和访问控制避免方法被误用。

1. 语义化命名+访问控制区分方法

不要新增通用的ReduceLoyaltyPoint,而是给订单调整场景专门创建一个语义明确的方法,比如AdjustLoyaltyPointForOrderModification,并通过C#的访问修饰符限制其调用范围:

  • 用internal修饰该方法,确保只有同一领域程序集内的代码(比如Order Aggregate)能调用,外部应用层无法直接访问,从根源上避免被用来替代兑换操作。
  • 公开的RedeemLoyaltyPoint保留原有的250积分限制,只用于用户主动兑换场景。

2. 代码实现示例

// 积分值对象:封装积分的基本加减逻辑,确保积分非负
public record LoyaltyPoint(int Value)
{
    public static LoyaltyPoint Create(int value)
    {
        if (value < 0)
            throw new ArgumentException("积分不能为负数");
        return new LoyaltyPoint(value);
    }

    public LoyaltyPoint Add(int amount) => Create(Value + amount);
    public LoyaltyPoint Subtract(int amount) => Create(Value - amount);
}

// 客户实体:封装积分的业务规则
public class CustomerEntity
{
    private LoyaltyPoint _loyaltyPoint;

    public CustomerEntity(LoyaltyPoint initialPoint)
    {
        _loyaltyPoint = initialPoint;
    }

    // 公开方法:下单增加积分
    public void AddLoyaltyPoint(int amount)
    {
        _loyaltyPoint = _loyaltyPoint.Add(amount);
    }

    // 公开方法:用户主动兑换积分,需满足250积分门槛
    public void RedeemLoyaltyPoint(int amount)
    {
        if (_loyaltyPoint.Value < 250)
            throw new InvalidOperationException("积分不足250,无法发起兑换");
        if (amount > _loyaltyPoint.Value)
            throw new InvalidOperationException("兑换积分不能超过当前持有量");
        
        _loyaltyPoint = _loyaltyPoint.Subtract(amount);
    }

    // 内部方法:仅用于订单修改的积分调整,无兑换门槛,但保证积分非负
    internal void AdjustLoyaltyPointForOrderModification(int adjustmentAmount)
    {
        var newPointValue = _loyaltyPoint.Value + adjustmentAmount;
        if (newPointValue < 0)
            throw new InvalidOperationException("调整后积分不能为负数");
        
        _loyaltyPoint = LoyaltyPoint.Create(newPointValue);
    }
}

// 订单聚合根:处理订单生命周期中的积分逻辑
public class OrderAggregate
{
    private readonly CustomerEntity _customer;
    private int _previousEarnedPoints;

    public OrderAggregate(CustomerEntity customer)
    {
        _customer = customer;
    }

    // 创建订单时记录并增加积分
    public void CreateOrder(int earnedPoints)
    {
        _customer.AddLoyaltyPoint(earnedPoints);
        _previousEarnedPoints = earnedPoints;
    }

    // 修改订单时计算积分差额并调用调整方法
    public void ModifyOrder(int newEarnedPoints)
    {
        var adjustment = newEarnedPoints - _previousEarnedPoints;
        _customer.AdjustLoyaltyPointForOrderModification(adjustment);
        _previousEarnedPoints = newEarnedPoints;
    }
}

3. 额外的防护建议

  • 给AdjustLoyaltyPointForOrderModification添加清晰的XML注释,明确说明该方法仅用于订单修改场景,禁止其他用途。
  • 编写单元测试覆盖两种场景:验证主动兑换必须触发250积分检查,订单调整则跳过该检查但保证积分不为负。
  • 如果团队规模较大,可以引入OrderLoyaltyAdjustmentService领域服务,让订单调整的积分操作必须通过该服务执行,进一步封装逻辑,减少误用风险。

内容的提问来源于stack exchange,提问作者fingers10

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:10:39