5

I have a DTO like below

class OnlineProfileDTO {
  @JsonProperty("likes")
  SummaryDTO likes;

  // lots of other complex fields similar to above
}

class SummaryDTO {
  @JsonProperty("count")
  int count;
}

Now whenever I need likes count in a DTO to DBEntity converter, I need to check if onlineProfileDTO.getLikes() is null or not before calling onlineProfileDTO.getLikes().getCount(). Alternatively, catch NullPointerException and ignore it. But this will have to be done for all complex fields which can be potentially null.

Calling a common method like below can avoid the above repetition -

getCount(SummaryDTO anySummaryDTO) {
  if (anySummaryDTO != null) {
    return anySummaryDTO.getCount();
  }
  return 0;
}

Once above method is defined, one can use it to get Like count in following fashion:

getCount(onlineProfileDTO.getLikes())

My question is where should this method be ideally placed? More specifically, is it OK to move this method to DTO class which usually contains the transfer object fields along with their getters and setters only?

Approach 2: One can also define getLikes() method in a way that it returns the count directly but its return datatype will be int.

5
  • 1
    What kind of mapper do you use? If you do it on your own then switch to some convinient solution like Dozer or Orika. Plus, consider rethinking your design - count of likes could be taken as array length (in some cases empty one). Avoid adding behaviour to your DTOs. Commented Mar 14, 2018 at 13:49
  • I got the second part of not adding behavior to DTOs but didn't get the array length part. there is no array involved here
    – comiventor
    Commented Mar 14, 2018 at 14:04
  • You don't have an array but maybe you should. If your likes object contains only count then most likely it should be just numberOfLikes. If your "like" has more complex structure, e.g. whoLiked, timestamp,etc. then probably you should have something like List<LikeInfo> and get number of likes as the length of the array. Commented Mar 14, 2018 at 14:23
  • it contains just numberOfLikes but it has to adhere to the JSON structure of the payload I am receiving. Any other DTO structure would not help. I created a method getCount and kept it outside the DTO. I liked the thing you said about not adding behavior to a DTO.
    – comiventor
    Commented Mar 14, 2018 at 14:34
  • What's your JDK? The if null check can be solved with Optional
    – Laiv
    Commented Mar 14, 2018 at 18:22

4 Answers 4

6

There are more cons than pros.

Cons:

  • If this Dto is passed across the network, some framework can try to serialize the method. Using Swagger on this Dto, for example, it will show an attribute example on documentation if you have a method getExample. This can be ignored in most of the frameworks but you need to remember that every time you add a new method in the Dto
  • A Dto is supposed to be "dummy". The getCount can be interpreted as part of the business code. So, the Dto it's not the better place.
  • The number of methods on Dto have a tendency to grow up and will be harder to modify the actual attributes that are being used in this methods, because there will be so many methods dealing with fields that you, maybe, would like to delete or move from another Dto.

Pros:

  • If you are looking for the code that are dealing with the Dto data, the Dto it's probably the first place that many developers will visit to start to understand the code.
5

This seems to me like an X/Y question. Your problem seems to really be with nulls here.

Try to avoid nulls as much as you can. As you have seen, they are a pain to work with.

For complex Sub-DTOs you can use the Null Object Pattern.

Also, you could let the (private) field be null and use java.util.Optional in the getter:

public Optional<Summary> getLikes() {
  return Optional.ofNullable(likes);
}

And then use it like this:

int count = onlineProfile.getLikes().map(Summary::count).orElse(0);

In this way you explicitly must define what happens when the value is not present, which is a good thing.

Finally, in simple cases you can use Jackson facilities to avoid ever having a null in your object, e.g. like this:

class OnlineProfileDTO {
  private int likes;

  @JsonCreator
  public OnlinceProfileDTO(
      @JsonProperty("likes") SummaryDTO likes,
      @JsonProperty("...") ...) {
    this.likes = likes == null ? 0 : likes.getCount();
  }
}
0

I am in the same situation where I am trying to separate Entity (JPA entity) from business object (Domain) and from DTO (For JSON purposes on the UI layer).

So it looks like it would make the architecture cleaner if you keep these 3 separate, but then this involves a lot of mapping. I think it makes sense to separate them in the beginning and only think about performance optimization later (by combining some of the aspects of these 3 layers). For example, in the extreme you can have only 1 entity which will comprise JPA, JSON and business specific details (some methods).

0

As the name suggests DTOs (Data Transfer Objects) are used to transfer data and only data. By adding a getCount that does some computation, you are no longer transferring just data but also functionality/behaviour.

One of the reasons to avoid putting behaviour into DTOs is simply because it is not something easily transferable between the consumers of your service that is providing those DTO objects. And why is that something to take into consideration?

Let's imagine that today your service is sending DTOs object only between your own classes (internally), but tomorrow comes a requirement that requires you to expose that service to external consumers (as a webservice for example). How do you tell the consumers how to get the count of likes? If the consumer is in javascript, C#, java, ruby, etc. the way it is done is different, and you do not want to be worrying about the compatibility between your DTO and your consumers.

It is then the responsibility of each system to do the necessary computation to obtain the values that they need or, if you want to avoid your consumers to do the computation, your service provider will have to transfer the result of the computation as data.

2
  • 1
    Thanks, can DTO object have a copy-with method ? Commented Oct 18, 2022 at 11:58
  • @AyoubBoumzebra I have a similiar question, could a DTO have a method Update to update all the fields with values passed as parameters? Commented Nov 10, 2022 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.