Is it a good practice to create many DTO for a model?
Asked Answered
T

3

11

Assuming I have a class User with many fields:

public class User {
    public Integer id;
    public String name;
    public String username;
    public Integer age;
    public Address address;
    public String phoneNumber;
    public String email;
}

But I will not always need all User attributes in frontend. Each screen needs only some of User's fields. Is it a good practice to make DTO classes for each screen, since they access different attributes? Like this:

class UserToScreenADTO implements Serializable {
    public String name;
    public String email;
}

class UserToScreenBDTO implements Serializable {
    public String phoneNumber;
    public Address address;
}

class UserToScreenCDTO implements Serializable {
    public Integer id;
    public String username;
    public String email;
}
Teresetereshkova answered 25/8, 2020 at 23:33 Comment(8)
Can you clarify what you mean by "I will not always need all fields..."? Are the representations used in the same frontend, by the same user? Or are the representations always used by different users or different front-ends?Eloquent
This would be a great use case for abstract classes. You'll want to find your common elements between the models and create a BaseUser, from which your other users are extended.Kasey
Hi, @Turing85. For example, Profile Screen would access name, email, phoneNumber and address. However, Users List Screen would access only name and username to display in a table. In the same frontend, I can have multiple screens displaying User objects in different ways.Teresetereshkova
@Kasey and with the given code, what attributes would you recommend putting into the abstract class?Eloquent
@NicolasHolanda so the standard use case is that a user can access all those screens and, in return, may eventually accumulate all information of a user?Eloquent
@Turing85, it's like an user management system, where an admin user can see a screen with a list of usernames and emails of users and it can click in some of the usernames to open a new window with more informations about the choosen user, like name, address, phoneNumber, age, etc... Did you get it?Teresetereshkova
Yes, it is good practice to create multiple DTOs for your model according to your need.Candra
@VishwaRatna ... says who? Any references for this claim?Euphony
M
9

Using "just" one DTO or the entity directly comes with a high cost that you usually only pay later, so I would recommend anyone to create a DTO per use case just like you did here.

Here are some of the reasons/costs for this:

  • If the user of the API sees accessors for state that isn't loaded this will trigger lazy loading which will either result in bad performance or lazy initialization exceptions. If objects are passed through a session, you might lose the context how the object came to be, so you might not always be able to tell which state is loaded.
  • It might be inefficient to load all data all the time. If you have some text columns that contain lots of data this has to be transferred over the wire and materialized as Java Objects etc. If you don't use the data, it's just pointless to load it at all. One might say this is negligible, but it depends on your use case. The worst that can happen? The DBMS does a full table scan or a less efficient index scan rather than index only scan because you instruct the DBMS to load the value for a column.
  • Not all the state that you want to provide for a client should be in the relational representation. If you do aggregations or use expressions for e.g. concatenating columns together, you need a DTO.

Having said that, this is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(User.class)
public interface UserToScreenADTO extends Serializable {
    String getName();
    String getEmail();
}

@EntityView(User.class)
public interface UserToScreenBDTO extends Serializable {
    String getPhoneNumber();
    Address getAddress();
}

@EntityView(User.class)
public interface UserToScreenCDTO extends Serializable {
    Integer getId();
    String getUsername();
    String getEmail();
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

UserToScreenADTO u = entityViewManager.find(entityManager, UserToScreenADTO.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Magog answered 31/8, 2020 at 8:25 Comment(0)
E
5

I would create just one DTO class but e.g. pass to its constructor
the list of the fields which I want to be pulled and set by the backend.
All other fields will be null.

The list of fields will be passed in by the front-end.

I find this approach pretty flexible/dynamic.

It also avoids multiple classes to maintain.

I don't know if this approach matches any best practices or enterprise patterns
but creating multiple DTO classes definitely sounds worse.

Euphony answered 26/8, 2020 at 0:14 Comment(0)
E
3

Since OP said that the system is used from the same frontend and in the same context, I would consider it bad practice and not recommend using different DTOs.

Reasoning:

Modern frontends normally manage a store of all entites received by the backend. So the frontend can look up entities in the store and - depending on caching policies - load them from the store rather than requesting them from the server. Thus, instead of fetching users part-by-part, the user is transmitted once. This can be further improved by using ETags. While the usage of Etags will hardly improve latency, it can improve network load since the response to a matching ETag is a 304/Not Modified without a body (!) rather than a 200/OK with body. While ETags can be used with many Dto-Objects, more (partial) updates might occur. If, for example, the email and the phone number of a user changes, and the frontend first requests the UserToScreenADTO, it would get a response body that - among other things - contains the new email. When it then later requests a UserToScreenBDTO, it would - again - receive a response body containing the new phone number. With only one DTO, the frontend will receive one updated representation on the first request, and all successive request (made with the matching ETag) will result in a 304/Not Modified.

Furthermore, more classes normally mean higher complexity. Thus, I would recommend to keep the number of classes reasonably small. If using an ETag is not wanted and/or the frontend does not keep a store of server-sent entities, I would recommend the approach described in peter petrov's answer.


The representation should only change if the context changes. If the representations differ drastically between, for example, a user-frontend and an admin-frontend, then different DTOs may be justified.

Eloquent answered 26/8, 2020 at 0:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.