daicy
发布于 2025-09-28 / 6 阅读
0
0

DDD中的Assembler(装配器)介绍

DDD中的Assembler(装配器):对象转换的桥梁

在领域驱动设计(DDD)中,Assembler(装配器) 是负责不同层对象之间转换的组件,主要作用是在领域对象(如实体、值对象)与数据传输对象(DTO)、数据库实体(DO)等不同类型对象之间进行属性映射和转换,同时保持各层之间的解耦。

Assembler的核心价值

在DDD分层架构中,不同层次使用的对象模型有着不同的职责:

  • 领域层使用领域对象(实体、值对象),包含业务逻辑和规则
  • 应用层和接口层使用DTO(数据传输对象),用于数据传递
  • 基础设施层使用数据库实体(DO),用于数据持久化

Assembler的核心价值在于:

  • 隔离不同层的对象模型,避免层与层之间的紧耦合
  • 集中管理对象转换逻辑,提高代码复用性
  • 隐藏对象转换的复杂细节,使各层聚焦于自身职责

Assembler的主要职责

  1. 领域对象与DTO之间的转换

    • 将领域对象(实体/值对象)转换为DTO,供应用层和接口层使用
    • 将DTO转换为领域对象或命令对象,供领域层处理
  2. 领域对象与数据库实体(DO)之间的转换

    • 将领域对象转换为数据库实体,用于持久化
    • 将数据库实体转换为领域对象,用于业务处理
  3. 处理复杂对象关系

    • 处理聚合对象的嵌套转换
    • 处理集合类型对象的批量转换
    • 处理不同命名规范和数据类型的映射

Assembler的实现方式

1. 手动实现的Assembler

最常见的方式是创建专门的Assembler类,手动编写对象转换逻辑:

// 订单领域对象
public class Order {
    private OrderId id;
    private UserId userId;
    private Money totalAmount;
    private List<OrderItem> items;
    private OrderStatus status;
    // 业务方法...
}

// 订单DTO
public class OrderDTO {
    private String orderId;
    private String userId;
    private BigDecimal totalAmount;
    private String currency;
    private List<OrderItemDTO> items;
    private String status;
    // getter和setter...
}

// 订单Assembler
public class OrderAssembler {
    
    // 领域对象转DTO
    public OrderDTO toDTO(Order order) {
        OrderDTO dto = new OrderDTO();
        dto.setOrderId(order.getId().getValue());
        dto.setUserId(order.getUserId().getValue());
        dto.setTotalAmount(order.getTotalAmount().getAmount());
        dto.setCurrency(order.getTotalAmount().getCurrency().getCode());
        dto.setStatus(order.getStatus().getName());
        dto.setItems(order.getItems().stream()
            .map(this::toOrderItemDTO)
            .collect(Collectors.toList()));
        return dto;
    }
    
    // DTO转领域对象
    public Order toDomain(OrderDTO dto) {
        Order order = new Order(
            new OrderId(dto.getOrderId()),
            new UserId(dto.getUserId()),
            new Money(dto.getTotalAmount(), Currency.of(dto.getCurrency())),
            OrderStatus.fromName(dto.getStatus())
        );
        
        // 添加订单项
        if (dto.getItems() != null) {
            dto.getItems().forEach(itemDTO -> 
                order.addItem(toOrderItem(itemDTO))
            );
        }
        
        return order;
    }
    
    // 订单项转换(辅助方法)
    private OrderItemDTO toOrderItemDTO(OrderItem item) {
        // 转换逻辑...
    }
    
    private OrderItem toOrderItem(OrderItemDTO itemDTO) {
        // 转换逻辑...
    }
}

2. 使用工具类的Assembler

可以利用对象映射工具(如MapStruct、ModelMapper)简化转换逻辑:

// 使用MapStruct注解的Assembler
@Mapper(componentModel = "spring")
public interface OrderAssembler {
    OrderAssembler INSTANCE = Mappers.getMapper(OrderAssembler.class);
    
    @Mapping(source = "id.value", target = "orderId")
    @Mapping(source = "userId.value", target = "userId")
    @Mapping(source = "totalAmount.amount", target = "totalAmount")
    @Mapping(source = "totalAmount.currency.code", target = "currency")
    @Mapping(source = "status.name", target = "status")
    OrderDTO toDTO(Order order);
    
    @Mapping(source = "orderId", target = "id.value")
    @Mapping(source = "userId", target = "userId.value")
    @Mapping(target = "totalAmount", expression = "java(new Money(dto.getTotalAmount(), Currency.of(dto.getCurrency())))")
    @Mapping(source = "status", target = "status", qualifiedByName = "statusFromString")
    Order toDomain(OrderDTO dto);
    
    @Named("statusFromString")
    default OrderStatus statusFromString(String status) {
        return OrderStatus.fromName(status);
    }
}

Assembler的设计原则

  1. 单一职责原则:一个Assembler只负责一类对象的转换(如OrderAssembler只处理订单相关对象)

  2. 无业务逻辑原则:Assembler只负责属性映射,不包含业务逻辑判断

  3. 双向转换能力:通常需要实现正向(领域对象→DTO)和反向(DTO→领域对象)转换

  4. 处理空值安全:考虑源对象或属性为空的情况,避免空指针异常

  5. 命名规范:通常以"对象名+Assembler"命名(如OrderAssembler、UserAssembler)

Assembler与其他组件的关系

  • 与Repository:Assembler常被Repository实现类使用,用于领域对象和数据库实体的转换

  • 与Application Service:应用服务使用Assembler在领域对象和DTO之间转换

  • 与Controller:控制器可能直接使用Assembler将DTO转换为命令对象,或将结果DTO返回给前端

何时需要使用Assembler

  • 当对象转换逻辑复杂,包含嵌套对象或特殊转换规则时

  • 当需要在多个地方复用相同的转换逻辑时

  • 当希望保持领域对象的纯净性,不包含与其他对象的转换方法时

对于简单的对象转换,有时也会直接在DTO中实现fromDomain()toDomain()方法,但对于复杂场景,专门的Assembler是更好的选择。

总之,Assembler在DDD中扮演着对象转换的桥梁角色,通过它可以有效隔离不同层次的对象模型,保持各层的独立性和内聚性。


评论