DDD中的Assembler(装配器)介绍
DDD 中的 Assembler(装配器):对象转换的桥梁
在领域驱动设计(DDD)中,Assembler(装配器) 是负责不同层级对象之间转换的关键组件。其核心职责是实现领域对象(如实体、值对象)与数据传输对象(DTO)、数据库实体(DO)等不同类型对象之间的属性映射与转换,同时在转换过程中保持各层架构之间的解耦。
Assembler 的核心价值
在 DDD 分层架构中,不同层次所使用的对象模型承担着不同的职责:
- 领域层:使用领域对象(实体、值对象),封装核心业务逻辑与规则。
- 应用层与接口层:使用DTO(Data Transfer Object,数据传输对象),专注于数据传递。
- 基础设施层:使用数据库实体(DO),专注于数据持久化。
Assembler 的核心价值主要体现在以下三个方面:
- 隔离模型:隔离不同层的对象模型,避免层级间的紧耦合。
- 集中管控:集中管理对象转换逻辑,提高代码的复用性与可维护性。
- 隐藏细节:隐藏对象转换的复杂细节,使各层能够聚焦于自身的核心职责。
Assembler 的主要职责
领域对象与 DTO 之间的转换
- 将领域对象(实体/值对象)转换为 DTO,供应用层和接口层使用。
- 将 DTO 转换为领域对象或命令对象(Command),供领域层处理。
领域对象与数据库实体(DO)之间的转换
- 将领域对象转换为数据库实体,用于数据持久化操作。
- 将数据库实体转换为领域对象,用于后续业务处理。
处理复杂对象关系
- 处理聚合对象内部的嵌套转换。
- 处理集合类型对象的批量转换。
- 处理不同命名规范和数据类型的映射差异。
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) {
// 转换逻辑...
return null;
}
private OrderItem toOrderItem(OrderItemDTO itemDTO) {
// 转换逻辑...
return null;
}
}2. 使用工具类的 Assembler
可以利用对象映射工具(如 MapStruct、ModelMapper)来简化转换逻辑,减少样板代码。以下示例展示了如何使用 MapStruct 注解定义 Assembler。
// 使用 MapStruct 注解的 Assembler
@Mapper(componentModel = "spring")
public interface OrderAssembler {
// standalone 模式下使用,Spring 模式下通常由容器注入
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")
// 复杂对象构建使用 expression
@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 的设计原则
- 单一职责原则:一个 Assembler 只负责一类对象的转换(例如
OrderAssembler仅处理订单相关对象)。 - 无业务逻辑原则:Assembler 仅负责属性映射,不应包含任何业务逻辑判断。
- 双向转换能力:通常需要实现正向(领域对象 → DTO)和反向(DTO → 领域对象)的转换能力。
- 空值安全:充分考虑源对象或属性为空的情况,避免抛出空指针异常(NPE)。
- 命名规范:通常采用「对象名 + Assembler」的命名方式(如
OrderAssembler、UserAssembler)。
Assembler 与其他组件的关系
- 与 Repository:Assembler 常被 Repository 实现类调用,用于领域对象和数据库实体(DO)之间的转换。
- 与 Application Service:应用服务使用 Assembler 在领域对象和 DTO 之间进行转换,以便跨层传递数据。
- 与 Controller:控制器可能直接使用 Assembler 将 DTO 转换为命令对象,或将处理结果转换为响应 DTO 返回给前端。
何时需要使用 Assembler
- 当对象转换逻辑复杂,包含嵌套对象或特殊转换规则时。
- 当需要在多个地方复用相同的转换逻辑时。
- 当希望保持领域对象的纯净性,避免领域对象中包含与其他对象的转换方法时。
说明:对于简单的对象转换,有时也会直接在 DTO 中实现fromDomain()和toDomain()方法。但对于复杂场景,使用专门的 Assembler 是更优的选择。
总之,Assembler 在 DDD 中扮演着对象转换桥梁的角色。通过合理使用 Assembler,可以有效隔离不同层次的对象模型,保持各层架构的独立性与内聚性。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/ddd-zhong-de-assembler-zhuang-pei-qi--jie-shao.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。