1

私が取り組んでいるプロジェクトで CQRS を使用しようとしていますが、現在、CQRS のクエリ側を実装する最良の方法に苦労しています。私のやや限定的な理解に基づいて、データベースにクエリを実行し、アプリケーションの UI レイヤーで使用されるクエリ結果を含む DTO を返すシン データ レイヤー (シン リード レイヤーと呼ばれることもあります) があります。

これは私が開発しているJava EEアプリケーションであるため、シンデータレイヤーはJPAを使用してデータベースにクエリを実行し、これを使用しEntityManager.createNamedQueryて結果を含むエンティティを返し、それをDTOにマッピングします。

アプリケーションのクエリ側が「読み取り専用」である必要がある場合、DTO には getter が含まれますが、各プロパティの setter は含まれず、作成時にプロパティを設定するために使用されるコンストラクターは含まれません。

単純なクエリの場合、コンストラクターを使用してエンティティの値を DTO に手動でマップできますが、これはより複雑なクエリでは実用的ではありません。特に、対応するエンティティにマップする必要がある「1 対多」の関係がエンティティに含まれている場合はそうです。 DTO。Dozer や ModelMapper などのマッピング フレームワークの使用を検討しましたが、それらはすべて DTO のセッターに依存しているようで、コンストラクターを使用していないようです。

次のコードは、状況を説明するために作成した 2 つのエンティティと 2 つの 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 にはゲッターとコンストラクターのみが必要ですか、それともセッターも使用できますか?

  2. DTO が理想的にはゲッターとコンストラクターのみを持つ必要がある場合、マッピング フレームワークは、エンティティが "1 対多" の関係を含む複雑な結果セットを返す DTO を設定するための最良の方法ですか?

  3. セッターではなくコンストラクターを使用できるマッピング フレームワークはありますか?

4

1 に答える 1

2
  1. 最良の方法は、ゲッターとコンストラクターのみを使用することです。次に、DTO は不変です。
  2. DTO は 2 つの方法で生成できます。1 つは、JPA と のようなクエリを使用することですselect new my_package.MyDto(u.username, u.email) from User u。多くのテーブル結果を含むより大きな dto でより一般的な 2 番目の方法は、MyBatis や SpringJdbcTemplate などの非 jpa テクノロジを使用することです。2 番目の方法は、データベースから不要なデータを取得していないため、より理にかなっています。したがって、パフォーマンス上の理由から、DTO の作成に必要なデータのみを取得する必要があります。
  3. はい - たとえば、最も人気のある JacksonMapper は、セッターの代わりにコンストラクターを使用できます。Orika などの他のフレームワークでも、このようなマッピングを作成できます。私のブログ投稿で詳細を確認できます - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html
于 2015-03-15T14:00:10.797 に答える