2015-03-15 2 views
1

Je cherche à utiliser CQRS dans un projet sur lequel je travaille, mais je suis actuellement aux prises avec la meilleure façon d'implémenter le côté requête de CQRS. Basé sur ma compréhension quelque peu limitée, il y a une couche de données minces (parfois appelée couche de lecture mince) qui interroge la base de données et renvoie un DTO avec les résultats de la requête qui sont utilisés par la couche d'interface utilisateur de l'application. Comme il s'agit d'une application Java EE, je développe le Thin Data Layer en utilisant JPA pour interroger la base de données en utilisant EntityManager.createNamedQuery qui renvoie une entité contenant les résultats que je suis ensuite mappage vers un DTO. Etant donné que le côté requête de l'application doit être "en lecture seule", le DTO contient des getters mais pas de setters pour chacune des propriétés et un constructeur qui est utilisé pour remplir les propriétés lors de la création. Pour une requête simple, je peux mapper manuellement les valeurs dans l'entité au DTO en utilisant le constructeur, mais ce n'est pas pratique pour les requêtes plus complexes, en particulier lorsque l'entité contient des relations "un-à-plusieurs" qui doivent être mappé aux DTO correspondants. J'ai envisagé d'utiliser des frameworks de cartographie tels que Dozer et ModelMapper, mais ils semblent tous dépendre du DTO ayant des setters et ne semblent pas utiliser le constructeur.Mappage entre entité JPA et DTO dans CQRS

Le code suivant représente une vue très simplifiée de deux entités et deux DTO que j'ai créés pour aider à expliquer la situation.

@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 

} 

Mes questions sont les suivantes:

  1. Lorsque vous utilisez CQRS si les DTO n'ont getters et un constructeur ou est-il acceptable d'avoir setters ainsi?

  2. Si les DTO devraient idéalement avoir seulement getters et un constructeur, est un framework de mapping la meilleure façon de remplir les DTO où l'entité a rendu un ensemble complexe de résultats contenant une relation « one-to-many » ?

  3. Y a-t-il des cadres de mappage qui peuvent utiliser un constructeur plutôt que des setters ?

Répondre

2
  1. La meilleure façon est d'avoir seulement getters et un constructeur. Vos DTO sont alors immuables.
  2. Vous pouvez produire des DTO de deux manières. L'un est d'utiliser JPA et des requêtes comme select new my_package.MyDto(u.username, u.email) from User u. La seconde qui est la plus populaire pour les plus gros dtos incluant de nombreux résultats de table est d'utiliser une technologie non-jpa comme MyBatis ou SpringJdbcTemplate. La deuxième façon est plus logique car vous ne récupérez pas les données inutiles de la base de données. Donc, pour des raisons de performances, vous devez obtenir uniquement les données nécessaires pour créer DTO. Oui, par exemple le plus populaire JacksonMapper peut utiliser des constructeurs au lieu de setters. D'autres cadres comme Orika peuvent également créer une telle cartographie. Vous pouvez trouver des détails sur mon blog - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html