将实体对象映射到领域接口

后端开发 2026-07-08

我在领域层有一个接口,以及两个用于表示该接口的基础设施实现。问题在于,该接口不能在mapper类中实例化,因此我不知道如何在手动映射中把实体映射到领域模型。
我开始学习高级编程,因此不应该使用switch,而应该考虑SOLID概念。
我写的代码:
OrderDiscount是领域中的一个接口

discountType、discountValue位于数据库中。这些字段应该映射到orderDiscount

领域:

private Order(String id,
              String customerId,
              List <OrderItem> items,
              Money totalPrice,
              OrderDiscount discount,
              OrderStatus status,
              LocalDateTime createdAt,
              LocalDateTime updatedAt
)
public static Order create(String customerId,
                               List <OrderItem> items,
                               OrderDiscount discount){

        LocalDateTime now = LocalDateTime.now();

        Money finalPrice = calculateFinalPrice(items, discount);

        return new Order(
                UUID.randomUUID().toString(),
                customerId,
                items,
                finalPrice,
                discount,
                OrderStatus.PENDING,
                now,
                now);
    }
public interface OrderDiscount {
    Money applyTo(Money total);
    OrderDiscountDetail getDetails();
    DiscountType type();
}
public interface OrderDiscountFactory {
    DiscountType supportedType();

    OrderDiscount create(OrderDiscountDetail detail,Currency currency);
}
public final class FixedAmountDiscount implements OrderDiscount {

    private final Money fixedAmountDiscount;

    private FixedAmountDiscount(Money fixedAmountDiscount) {

        if (fixedAmountDiscount == null) {
            throw new IllegalArgumentException(
                    "Flat Discount cannot be null"
            );
        }

        if (fixedAmountDiscount.getAmount().compareTo(BigDecimal.ZERO) < 0 ) {
            throw new IllegalArgumentException(
                    "Flat Discount amount must be zero or greater"
            );
        }
        this.fixedAmountDiscount = fixedAmountDiscount;
    }

    public static FixedAmountDiscount create (Money fixedAmountDiscount) {
        return new FixedAmountDiscount(fixedAmountDiscount);
    }

    @Override
    public Money applyTo(Money total) {
        if (total == null) {
            throw new IllegalArgumentException(
                    "Total amount cannot bet null"
            );
        }

        if (!total.getCurrency().equals(fixedAmountDiscount.getCurrency()) ) {
            throw new IllegalStateException(
                    "Currency mismatch "
            );
        }

        BigDecimal amount = total.getAmount()
                                 .subtract(fixedAmountDiscount.getAmount())
                                 .max(BigDecimal.ZERO);

        return Money.create(amount, total.getCurrency());
    }

    @Override
    public OrderDiscountDetail getDetails() {
        return new OrderDiscountDetail(DiscountType.FIXED_AMOUNT,
                fixedAmountDiscount.getAmount());
    }

    @Override
    public DiscountType type() {
        return DiscountType.FIXED_AMOUNT;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }

        if(!(other instanceof FixedAmountDiscount o)) {
            return false;
        }
        return fixedAmountDiscount.equals(o.fixedAmountDiscount);
    }

    @Override
    public int hashCode() {
        return fixedAmountDiscount.hashCode();
    }
}
@Component
public class DiscountRegistry {

    private final Map<DiscountType, OrderDiscountFactory> factories;

    public DiscountRegistry(List<OrderDiscountFactory> factories) {

        this.factories =
                factories.stream()
                        .collect(Collectors.toMap(
                                OrderDiscountFactory::supportedType,
                                Function.identity()));
    }

    public OrderDiscount create(OrderDiscountDetail detail, Currency currency) {

        return factories
                .get(detail.type())
                .create(detail, currency);
    }
}
public class FixedAmountDiscountFactory implements OrderDiscountFactory {
    private final DiscountRegistry registry;

    public FixedAmountDiscountFactory(DiscountRegistry registry) {
        this.registry = registry;
    }

    @Override
    public DiscountType supportedType() {
        return DiscountType.FIXED_AMOUNT;
    }

    @Override
    public OrderDiscount create(OrderDiscountDetail detail, Currency currency) {
        return registry.create(detail, currency);
    }
}

基础设施/实体:

    @Getter
    @Enumerated(EnumType.STRING)
    @Column(name = "discount_type", nullable = false, length = 30)
    @NotNull(message = "discount type is required")
    private DiscountType discountType = DiscountType.NONE;

    @Getter
    @Column(name = "discount_value", nullable = false, precision = 19, scale = 2)
    @NotNull(message = "discount value is required")
    private BigDecimal discountValue = BigDecimal.ZERO;

我写的orderMapper:

public class OrderMapper {

    private final OrderDiscountFactory discountFactory;

    public OrderMapper(OrderDiscountFactory discountFactory) {
        this.discountFactory = discountFactory;
    }

    public static OrderEntity toEntity(Order order) {

        OrderDiscountDetail discountDetail = order.getDiscount().getDetails();

        OrderEntity entity = new OrderEntity(
                order.getId(),
                order.getCustomerId(),
                order.getTotalPrice().getAmount(),
                CurrencyMapper.toEntity(
                        order.getTotalPrice().getCurrency()
                ),
                order.getStatus(),
                discountDetail.type(),
                discountDetail.value()
        );
        for (OrderItem orderItem : order.getItems()) {
            entity.addItem(toItemEntity(orderItem));
        }

        return entity;
    }

    private static OrderItemEntity toItemEntity(OrderItem item) {
        return new OrderItemEntity(
            item.getProductId(),
            item.getProductName(),
            item.getQuantity(),
            item.getPrice().getAmount(),
            CurrencyMapper.toEntity(item.getPrice().getCurrency()));
    }

    public Order toDomain(OrderEntity entity) {

        Currency currency = Currency.getInstance(entity.getCurrency());

        Money money = Money.create(entity.getTotalAmount(), currency);

        OrderDiscountDetail detail =
                new OrderDiscountDetail(
                        entity.getDiscountType(),
                        entity.getDiscountValue()
                );

        OrderDiscount discount =
                discountFactory.create(detail, currency);

        List <OrderItem> items = entity.getItems()
                .stream()
                .map(OrderItemMapper::toDomain)
                .toList();

        return Order.restore(
                entity.getId(),
                entity.getCustomerId(),
                items,
                money,
                discount,
                entity.getStatus(),
                entity.getCreatedAt(),
                entity.getUpdatedAt()
        );
    }

}

我发现这个映射器很糟糕,因为它变成了对领域的依赖。请给我一个企业级的答案。我指的是那种高级代码。

解决方案

我认为领域接口应该保持纯粹。'OrderDetail' 属于Order领域:

public interface OrderDiscount {
    Money applyTo(Money total);
    DiscountType type();
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章