DDD持久化映射:基于SOLID实现OrderDiscount领域对象的解耦映射
符合DDD规范与SOLID原则的实体到领域折扣对象映射方案
现有方案的核心问题根因
原OrderMapper直接绑定单个具体OrderDiscountFactory实例,本质是把「折扣类型识别-工厂绑定」的硬编码逻辑放在基础设施层,直接违反:
- 依赖倒置原则:Mapper直接依赖具体工厂实现,而非抽象匹配逻辑
- 开闭原则:新增折扣类型时必须修改Mapper代码补充分支判断(即要求规避的switch逻辑)
- 解耦要求:领域组件与基础设施层的映射逻辑强绑定,无法独立替换折扣实现
整体实现思路
依托已经定义的DiscountRegistry核心组件,通过「类型-工厂」的自动映射机制,完全消除分支判断,彻底解耦Mapper与具体折扣实现的依赖。
具体实现步骤
1. 修正领域层抽象契约
对现有OrderDiscountFactory接口补充最小必要契约,明确每个工厂的自描述能力,避免外部组件判断类型:
public interface OrderDiscountFactory { // 契约1:声明当前工厂支持的折扣类型,由每个实现类自行返回 DiscountTypeEnum supportType(); // 契约2:根据持久层存储的discountValue创建对应领域折扣对象 OrderDiscount create(BigDecimal discountValue); }
2. 强化DiscountRegistry的纯领域能力
将DiscountRegistry设计为无框架依赖的纯领域POJO,仅负责维护折扣类型-对应工厂的映射关系,不包含任何业务判断逻辑:
public class DiscountRegistry { // 内部存储映射关系,key为持久层存储的discountType枚举 private final Map<DiscountTypeEnum, OrderDiscountFactory> factoryMapping = new HashMap<>(); // 对外提供注册入口 public void register(DiscountTypeEnum discountType, OrderDiscountFactory factory) { factoryMapping.put(discountType, factory); } // 对外提供工厂获取入口,全程无分支判断 public OrderDiscountFactory getFactory(DiscountTypeEnum discountType) { OrderDiscountFactory factory = factoryMapping.get(discountType); if (factory == null) { throw new IllegalArgumentException("未匹配到对应折扣类型的工厂,类型值: " + discountType); } return factory; } }
3. 基础设施层自动装配映射关系
在基础设施层实现注册表的自动初始化,完全避免手动编写分支判断收集工厂:
// 基础设施层自动配置组件(以Spring生态为例) @Component public class DiscountRegistryInitializer implements ApplicationRunner { private final DiscountRegistry discountRegistry; // 自动注入所有OrderDiscountFactory的实现类实例,无需手动列举 private final List<OrderDiscountFactory> allFactories; @Override public void run(ApplicationArguments args) { // 遍历所有工厂,依靠工厂自身的supportType()方法完成注册 allFactories.forEach(factory -> discountRegistry.register(factory.supportType(), factory) ); } }
对应已实现的FixedAmountDiscount,补充其专属工厂实现:
@Component public class FixedAmountDiscountFactory implements OrderDiscountFactory { @Override public DiscountTypeEnum supportType() { return DiscountTypeEnum.FIXED_AMOUNT; } @Override public OrderDiscount create(BigDecimal discountValue) { return new FixedAmountDiscount(discountValue); } }
4. 重构OrderMapper的依赖关系
将原Mapper对具体工厂的直接依赖,替换为对抽象DiscountRegistry的依赖,彻底消除硬编码耦合:
@Repository public class OrderMapperImpl { // 仅依赖抽象注册表,不感知任何具体折扣工厂的存在 private final DiscountRegistry discountRegistry; // 构造器注入 public OrderMapperImpl(DiscountRegistry discountRegistry) { this.discountRegistry = discountRegistry; } public Order toDomain(OrderEntity orderEntity) { // 直接读取持久层存储的两个字段 DiscountTypeEnum discountType = orderEntity.getDiscountType(); BigDecimal discountValue = orderEntity.getDiscountValue(); // 通过注册表匹配对应工厂,全程无if/switch分支 OrderDiscount orderDiscount = discountRegistry.getFactory(discountType).create(discountValue); // 组装Order聚合根 return new Order( orderEntity.getOrderId(), orderEntity.getOriginalAmount(), orderDiscount ); } }
方案的规范符合性说明
- 符合SOLID原则的要求:
- 单一职责:Mapper仅负责实体与聚合根的转换,注册表仅负责类型与工厂的映射,每个工厂仅负责创建对应类型的折扣对象,无冗余职责
- 开闭原则:后续新增折扣类型(如按比例折扣)时,仅需新增对应的
OrderDiscount实现类、对应工厂类即可自动完成注册,无需修改Mapper、注册表的任何现有代码 - 依赖倒置:Mapper依赖抽象的注册表组件,不依赖具体工厂实现;领域层不感知持久层的映射细节,依赖方向保持干净
- 里氏替换:所有工厂实现只要符合接口契约,即可被注册表无缝替换,不影响现有逻辑运行
- 接口隔离:工厂接口仅暴露必要的两个方法,无强制要求实现类完成的无关逻辑
- 完全规避switch分支:所有类型匹配逻辑在应用启动时通过自动遍历完成,运行时仅做Map取值操作,无硬编码分支判断
- 彻底解耦:原Mapper对单个工厂的强依赖被替换为抽象依赖链,任意折扣实现的调整不会影响Mapper的核心逻辑
注意点:保持领域层纯净,不要将框架依赖(如Spring的注解、自动配置逻辑)写入领域层的
DiscountRegistry与OrderDiscountFactory接口,注册表的装配工作全部放在基础设施层完成。
内容的提问来源于stack exchange,提问作者F.M
相关产品推荐
相关产品推荐

