Отображение между сущностью JPA и DTO в CQRS

Я рассчитываю использовать CQRS в проекте, над которым я работаю, однако в настоящее время я борюсь с лучшим способом реализации стороны запросов CQRS. Исходя из моего немного ограниченного понимания, существует тонкий слой данных (иногда называемый тонким слоем чтения), который запрашивает базу данных и возвращает DTO с результатами запроса, которые используются уровнем пользовательского интерфейса приложения.

Поскольку это приложение Java EE, я разрабатываю тонкий слой данных, использующий JPA для запроса базы данных, используя EntityManager.createNamedQuery который возвращает сущность, содержащую результаты, которые я затем отображаю в DTO.

Учитывая, что сторона запроса приложения должна быть "только для чтения", DTO содержит геттеры, но не сеттеры для каждого из свойств и конструктор, который используется для заполнения свойств при создании.

Для простого запроса я могу вручную сопоставить значения в сущности с DTO, используя конструктор, однако это не практично для более сложных запросов, особенно когда сущность содержит отношения "один ко многим", которые должны быть сопоставлены с соответствующими DTOS. Я рассмотрел использование структур отображения, таких как 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 иметь только геттеры и конструктор или также допустимо иметь сеттеры?

  2. Если DTO в идеале должны иметь только геттеры и конструктор, является ли структура сопоставления лучшим способом для заполнения DTO, где объект возвращал сложный результирующий набор, содержащий отношение "один ко многим"?

  3. Существуют ли какие-либо каркасные структуры, которые могут использовать конструктор, а не сеттеры?

1 ответ

Решение
  1. Лучший способ - иметь только геттеры и конструктор. Тогда ваши DTO неизменны.
  2. Вы можете создать DTO двумя способами. Одним из них является использование JPA и такие запросы, как select new my_package.MyDto(u.username, u.email) from User u, Второе, более популярное для больших dtos, включая множество табличных результатов, - это использование не-jpa технологий, таких как MyBatis или SpringJdbcTemplate. Второй способ имеет больше смысла, потому что вы не извлекаете ненужные данные из базы данных. Поэтому из соображений производительности вы должны получать только те данные, которые необходимы для создания DTO.
  3. Да, например, самый популярный JacksonMapper может использовать конструкторы вместо сеттеров. Другие фреймворки, такие как Orika, также могут создавать такое отображение. Вы можете найти подробности в моем блоге - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html
Другие вопросы по тегам