Search code examples
javaspringspring-bootapi-design

Design of DTO from Model for an API


I am designing an API using Spring boot for some social network backend. My current model looks like this:

public class User {

    private long id;
    private String handle;
    private String name;
    private List<User> followers;
    private List<User> following;

    // Getters setters etc

Now, I have created DTO which closely resembles above structure. My problem is that sometimes I want to return exactly what's above (which is fine) but sometimes, I don't want that.

For example when someone is only interested in finding followers of user, I don't want to include followers and following (I am simply interested in id, handle and name so computing followers and following for all of those users would be an incredible waste of resources).

In my current implementation those fields are returned with null values, which I don't think is a great idea. Should I create a separate DTO without those lists with just id, handle and name? Or is there more elegant way to do it?


Solution

  • It is a controversial issue. If you don't want to create separate dto there are several ways to do it. It depends on what data access approach you are going to use:

    Using Spring Data JPA it is possible to return an entity in projection. You just need to add an additional constructor to your entity:

    public interface UserRepository extends JpaRepository<User, Long> {
        @Query("select new User(u.id,u.name) from User u")
        List<User> findAllUserItems();
    }
    

    Or same using JPA EntityManger:

        public List<User> findAllUserItems() {
            return entityManager.createQuery("select new User(u.id,u.name) from User u", User.class)
                    .getResultList();
        }
    

    If you wonder about unnecessary null fields and you are using Jackson, it is possible to configure to ignore null fields. For Spring Boot:

    spring.jackson.default-property-inclusion=non_null
    

    Or with Java config:

        @Bean
        public Jackson2ObjectMapperBuilder objectMapperBuilder() {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            builder.serializationInclusion(JsonInclude.Include.NON_NULL);
            return builder;
        }
    

    Or for not Spring Boot project:

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(converter());
        }
    
        @Bean
        public HttpMessageConverter converter() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            return new MappingJackson2HttpMessageConverter(objectMapper);
        }
    }
    

    Also, if you going to use Hibernate Session. To map directly to dto you may use AliasToBeanResultTransformer:

      public List<UserDto> findAllUserItems() {
                return session.createQuery("select u.id as id,u.name as name from User u")
                        .setResultTransformer(Transformers.aliasToBean(UserDto.class))
                        .list();
     }