DDD战术建模:促销系统多实体判定及代码复用方案咨询
促销系统领域建模问题解答
一、判定是否正确:是正确的
在DDD领域战术建模中,聚合根的划分核心依据是业务规则的一致性边界。四种促销类型各自拥有独特的业务规则(比如满减、折扣、赠品、满赠的生效逻辑、计算规则完全不同)、差异化数据(如满减的门槛金额、折扣的折扣率),这些规则和数据需要独立的一致性校验和生命周期管理,因此将它们划分为不同的聚合根/实体是完全符合DDD原则的。
二、实现代码与共享属性复用的建模方案
1. 抽象基类封装通用属性与行为
将所有促销共享的属性(Name、Description、促销周期)和通用行为(如校验有效期、获取基础信息)封装到抽象基类中,具体促销类型继承该基类并实现自身独有的业务逻辑。
示例代码(C#):
// 抽象基类:通用促销属性与行为 public abstract class Promotion { public string Name { get; protected set; } public string Description { get; protected set; } public PromotionPeriod Period { get; protected set; } // 通用行为:判断促销是否处于有效期内 public bool IsActive() { var now = DateTime.UtcNow; return Period.InitialDate <= now && now <= Period.FinalDate; } // 抽象方法:每个促销必须实现的核心业务逻辑(比如计算优惠) public abstract decimal CalculateDiscount(Order order); } // 值对象:封装促销周期及校验逻辑 public record PromotionPeriod(DateTime InitialDate, DateTime FinalDate) { // 工厂方法:确保日期合法性 public static PromotionPeriod Create(DateTime initial, DateTime final) { if (initial > final) throw new InvalidOperationException("促销起始日期不能晚于结束日期"); return new PromotionPeriod(initial, final); } }
2. 值对象复用共享属性组合
把具有业务含义的共享属性组合封装为值对象(比如上面的PromotionPeriod),所有促销类型直接复用该值对象,避免重复编写日期校验等逻辑,同时保证属性的一致性。
3. 领域服务提取跨聚合通用行为
如果存在多种促销都需要的通用行为(比如促销状态变更通知、促销规则的通用校验逻辑),不要将这些逻辑放到基类中,而是抽离为独立的领域服务,让各个聚合根调用该服务完成操作。
示例:
public class PromotionCommonService { public void LogPromotionUsage(Promotion promotion, Order order) { // 通用日志记录逻辑,所有促销类型都可调用 Console.WriteLine($"促销[{promotion.Name}]已应用于订单[{order.OrderId}]"); } }
4. 策略模式处理可变业务规则
对于某些可抽象的可变规则(比如优惠计算、资格校验),可以用策略模式将规则逻辑与聚合根分离。基类定义统一的规则调用接口,具体促销类型注入对应的策略实现,既保持聚合根的独立性,又能复用规则调用的逻辑框架。
示例:
// 优惠计算策略接口 public interface IDiscountCalculationStrategy { decimal Calculate(Order order, Promotion promotion); } // 满减策略实现 public class FullReductionStrategy : IDiscountCalculationStrategy { public decimal Calculate(Order order, Promotion promotion) { var fullReductionPromo = promotion as FullReductionPromotion; if (fullReductionPromo == null || !promotion.IsActive()) return 0; return order.TotalAmount >= fullReductionPromo.Threshold ? fullReductionPromo.ReductionAmount : 0; } } // 聚合根中注入策略 public class FullReductionPromotion : Promotion { private readonly IDiscountCalculationStrategy _calculationStrategy; public decimal Threshold { get; private set; } public decimal ReductionAmount { get; private set; } public FullReductionPromotion(string name, string description, PromotionPeriod period, decimal threshold, decimal reductionAmount, IDiscountCalculationStrategy calculationStrategy) { Name = name; Description = description; Period = period; Threshold = threshold; ReductionAmount = reductionAmount; _calculationStrategy = calculationStrategy; Validate(); } public override decimal CalculateDiscount(Order order) { return _calculationStrategy.Calculate(order, this); } }
注意事项
- 基类只保留真正通用的属性和行为,避免过度抽象导致基类臃肿(比如不要把某几种促销特有的逻辑强行放到基类);
- 每个聚合根保持自身业务逻辑的完整性,内部状态的修改仅由自身方法控制,不依赖其他聚合根;
- 避免在基类中引入外部依赖(比如数据库、第三方服务),这类逻辑应放到领域服务或基础设施层。
内容的提问来源于stack exchange,提问作者raphael-melo
相关产品推荐
相关产品推荐

