将实体对象映射到领域接口
我在领域层有一个接口,以及两个用于表示该接口的基础设施实现。问题在于,该接口不能在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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。