1

我希望在我正在从事的项目中使用 CQRS,但是我目前正在努力寻找实现 CQRS 查询端的最佳方法。根据我有限的理解,有一个 Thin Data Layer(有时称为 Thin Read Layer),它查询数据库并返回一个 DTO 以及应用程序的 UI 层使用的查询结果。

由于这是一个 Java EE 应用程序,我正在开发 Thin Data Layer 使用 JPA 来查询数据库,使用EntityManager.createNamedQuery该数据库返回一个包含结果的实体,然后我将其映射到 DTO。

鉴于应用程序的查询端应该是“只读的”,DTO 包含每个属性的 getter 但不包含 setter,以及用于在创建时填充属性的构造函数。

对于一个简单的查询,我可以使用构造函数将实体中的值手动映射到 DTO,但是这对于更复杂的查询是不切实际的,特别是当实体包含需要映射到对应的“一对多”关系时DTO。我已经研究过使用 Dozer 和 ModelMapper 等映射框架,但是它们似乎都依赖于具有设置器的 DTO,并且似乎没有使用构造函数。

下面的代码代表了我创建的两个实体和两个 DTO 的非常简化的视图,以帮助解释这种情况。

@Entity
@Table(name = "ORDER")
public class Order {

  // Various named queries
  @Id
  @Column(name = "ORDER_ID")
  private UUID orderId;

  @Column(name = "ORDER_NUMBER")
  private long orderNumber;

  @Column(name = "ORDER_DATE")
  @Temporal(TemporalType.DATE)
  private Date orderDate;

  @Column(name = "CUSTOMER_NAME")
  private String customerName;

  @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval=true)
  @JoinColumn(name = "ORDER_NUMBER", referencedColumnName = "ORDER_NUMBER")
  private List<OrderLine> orderLines;

  // Getters and setters, equals, hashCode, toString

}

@Entity
@Table(name = "ORDER_LINE")
public class OrderLine {

  @Id
  @Column(name = "ORDER_LINE_ID")
  private UUID orderLineId;

  @OneToOne(fetch=FetchType.EAGER)
  @JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID")
  private Order order;

  @Column(name = "PART_NUMBER")
  private String partNumber;

  @Column(name = "DESCRIPTION")
  private String description;

  @Column(name = "UNIT_PRICE")
  private BigDecimal unitPrice;

  @Column(name = "QUANTITY")  
  private int quantity;

  // Getters and setters, equals, hashCode, toString

}

public class OrderDTO {

  private long orderNumber;
  private Date orderDate;
  private String customerName;
  private List<OrderLine> orderLines;

  public OrderDTO() {}

  public OrderDTO(long orderNumber, Date orderDate, String customerName, List<OrderLineDTO> orderLines) {
    this.orderNumber = orderNumber;
    this.orderDate = orderDate;
    this.customerName = customerName;
    this.orderLines = orderLines;
  }

  //Getters but no setters

}

public class OrderLineDTO {

  private String partNumber;
  private String description;
  private BigDecimal unitPrice;
  private int quantity;

  public OrderLineDTO() {}

  public OrderLineDTO(String partNumber, String description, BigDecimal unitPrice, int quantity) {
    this.partNumber = partNumber;
    this.description = description;
    this.unitPrice = unitPrice;
    this.quantity = quantity;
  }

  //Getters but no setters

}

我的问题是:

  1. 使用 CQRS 时,DTO 应该只有 getter 和构造函数,还是也可以接受 setter?

  2. 如果 DTO 理想情况下应该只有 getter 和构造函数,那么映射框架是否是填充 DTO 的最佳方式,其中实体返回包含“一对多”关系的复杂结果集?

  3. 有没有可以使用构造函数而不是设置器的映射框架?

4

1 回答 1

2
  1. 最好的方法是只有 getter 和构造函数。那么你的 DTO 是不可变的。
  2. 您可以通过两种方式生成 DTO。一种是使用 JPA 和查询,例如select new my_package.MyDto(u.username, u.email) from User u. 第二种对于包含许多表结果的较大 dto 更受欢迎的是使用非 jpa 技术,如 MyBatis 或 SpringJdbcTemplate。第二种方法更有意义,因为您没有从数据库中检索不必要的数据。因此,由于性能原因,您应该只获取创建 DTO 所需的数据。
  3. 是的 - 例如最流行的 JacksonMapper 可以使用构造函数而不是 setter。Orika 等其他框架也可以创建这样的映射。您可以在我的博客文章中找到详细信息 - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html
于 2015-03-15T14:00:10.797 回答