Java DTO Guide: Fix Your API Design with One Simple Pattern

 

Transcript:

Today I will tell you what a data transfer object is and why it is not just some extra class and extra work but actually a very powerful component of your application architecture. There's going to be lots of practice, lots of code and I hope that by the end of this video you will understand that DTO is your best friend.

Okay. So imagine a typical situation. We have the entity class for instance employee and it has some fields like ID, password, email, name and in our controller we return the customer to the client. Well, that's a no-no. So the client receives the answer and then surprise, it contains the password. Whoever is at that site is happy. The security guy is shocked and you are looking for a job. Well, that sounds like a nightmare, doesn't it?

So, jokes aside, why do you need data transfer objects? For several reasons. First, you protect your data and enable a high level of confidentiality. Secondly, you isolate the business logic. Thirdly, you create a more flexible API. And as an additional benefit uh the system becomes more scalable. Okay. So now when we understand the problem let's actually create a DTO. We are going to use a Java record. Java records are perfect for DTO's because they act as immutable data carriers. Our employee DTO will contain only three fields uh ID, name and email. See no password. Then we create the mapper, employee mapper. Later in this video I will show you a very cool tool for automating the mapping but actually you can create mappers using pure Java code.

So let's create uh the employee mapper and uh in our case it will contain the single method map to DTO. It receives a customer and then it retrieves the necessary data from the customer object and creates a DTO object and then passes it to the server. So now our service can return the employee DTO in the method find all. So we are using the repository to find all employees. Then we stream them map to employee DTO and then collect them to the list and pass to the controller. And finally we fix our controller and instead of returning the employee we now return the employee DTO and everybody's happy. There are several types of DTO`s. So first there is the response DTO that's the object that we send to the client. Then there is the request DTO which is uh data that the server receives from the client and then transforms into the object. There is the composite DTO which uh helps you uh in the case when you have nested entities and you don't want to pass the whole list of objects together with your main DTO. And then there are also aggregate DTO`s that uh can accumulate data from various external APIs in one data object and then pass this uh data to the server. By the way, about the composite detail, have you ever been in a situation where the API returned too much data? Write in the comments what was the most ridiculous mistake that you have ever encountered related to the APIs. I will show the best comments in my next video. Okay.

So now I will show you how to deal with nested entities and just as I promised I will show you this very cool library for mapping the DTO`s automatically. This library is called mapstract. Mapstract is a library that automates DTO creation and reduces a lot of the boilerplate code that you otherwise have to write yourself. To use mapstract, you need to add the dependency to the configuration file. We also need to add the mapstract processor to the maven plugin that generates the mapper implementation at build time. Also, if you use Lombok, you should add the Lombok mapstract binding to the plugin as well. Great, we're all set. And now, let's look at another entity called user. It contains four fields: ID, username, email, and password. We are going to start with a very basic example of abstract usage so that you could uh grasp the idea behind it.

So imagine you are in a perfect world where DTO's and entities have the same fields. Well actually that is quite a realistic situation. Let's look for instance at user request DTO. It contains ID, username, email and password just like the user does. And then it can be transformed into the user entity. So in this case we can create a mapper. Let's call it user mapper. And we're going to annotate it with mapper. That's for map strct. And here in this interface you create the instance of the user mapper that you're going to use in other classes. This uh mapper contains a single method called the map to user and we are passing the user request and returning the user. That's it. And then when you run Maven clean install, the mapper implementation is going to be created in the target directory. Let's look at that. As you can see, mapstract created a method for converting DTO to user and it uses the same logic that we did, right? So, it extracts the data from the DTO, creates the user, passes the data to the user and returns the user. So, you don't have to do it yourself. You don't have to do it manually, which is just super cool.

After that, in the create user method of your service class, you map the user request to the user using the instance of our user mapper and save it to the database. Okay, so what about the situation where the DTO and the entity have different fields? So just like in the case of response, we don't want to pass the user password to the client. So the DTO has a different set of fields. Good news is that the process is almost the same. So you create a method in your user mapper called map to user response and then you add just one single line to the mapper annotation unmapped target policy reporting policy ignore. In this case, the mapper is going to analyze the entity and the DTO and it will map only those fields that are available. So let's run Maven clean install again and look at the new uh mapper implementation. You can see that the map added another method to the implementation which is map to user response. And you can see here that it uh uh retrieves uh only the necessary fields. You can see that there is no password mentioned, right? So it doesn't work with password because uh the uh response doesn't contain this field. Maximum security.

And finally, let's look at a more advanced scenario with nested entities. In this case, we have uh the class order and customer. The order class contains the ID and uh reference to the customer object. The customer entity contains some familiar fields like name, password, email and also the list of orders. So what can we do in this case? Let's first create our DTOS. So we are going to create the order response. It's going to contain the ID and total price only these fields. And then we create the customer response DTO. It contains the ID, name, email, and uh the list of order responses. Also, it contains an additional field uh that is not included into the entity, which is the total sum of expenses. and we are going to calculate it on the fly.

So as you can see we have two additional problems if we can say that we need to pass the order responses to the customer response right not just orders and we need to create a new field and calculate something for that well nothing that mapstract can't handle let's upgrade our mappers a little bit first let's create the order mapper it's going to be the abstract class in this case not the interface and we are going to add the component model spring to the mapper annotation as I'm using spring I'm taking advantage of spring dependency injection and spring beans so this option makes the order mapper the usual spring bean that we can use in other classes just like any other bean and as we have now the class instead of the interface. We can write the custom logic for the mapping methods. Well, in the case of the order, it's going to be nothing fancy. Everything that we have already seen. We are going to retrieve the necessary fields from the order and add them to the order response record. As you can see, I'm using builder here. That's the Lombok functionality. So if you use Lombok um that's uh just a very convenient way of building DTO`s and entities.

And now we are moving to the most interesting part. The customer mapper. The customer mapper is also an abstract class. It's not an interface. And we are adding the same option component model spring and make it a spring beam. Next, what we want to do is to create the methods mapped to customer response. Here we also use the builder to retrieve the fields that do not change. So these are the ID, name, and email. Then we check whether the order list of the customer is empty or not. If you don't perform this check and try to map the list of ordersh into the order response, you will get the null pointer exception and we don't want that, right? That's a nasty exception. Uh no, no in my code and in yours too. So we are checking whether the list is empty or not and if it's not we are streaming the orders mapping them to order response and gathering them to the list. This list is then added to the customer response. Okay, this task is done.

Right, the next task is to calculate the total sum of expenses. Here again we stream the orders for the customer. Then we map them right. So we retrieve the price for each order and then we apply the terminal operation of uh the stream API uh which is called reduce. With the help of uh this operation we can calculate the sum of all the elements that we passed in the stream. In this case the doubles for the price. This is going to be our final sum and we add it to the uh DTO. And finally, you return the response that you have built. Well, that is just super easy, right? Easy peasy lemon squeezy. Oops. And finally, the easiest part. We inject the dependency on the customer mapper to the customer service, right? So, you remember that's just a regular spring bean that needs to be injected and we do that. And that's it.

Now you can use it to map the customer to customer response. DTO's are used everywhere. They are used in REST APIs in Kafka in microservices to pass the data between them. So you can actually say that DTO is the agnostic language for the data transfer. Just imagine DTO's are universal language of data exchange. Thanks for watching this video. I really hope that DTO's became more than just extra classes for you and they really are more than just classes. You can say that they are kind of a philosophy of clean and secure code.

Summary

In this video, we explore what Data Transfer Objects (DTOs) are and why they’re essential for secure and clean application architecture. Through hands-on examples, you’ll learn how to use Java records for creating DTOs, map entities using plain Java or MapStruct, and avoid exposing sensitive data like passwords. The video also covers advanced mapping techniques, including handling nested entities and computed fields using MapStruct with Spring and Lombok. By the end, you'll see DTOs not as extra work, but as a powerful tool for building safe, scalable, and maintainable APIs.

About Catherine

Java developer passionate about Spring Boot. Writer. Developer Advocate at BellSoft

Social Media

Videos
card image
Jun 13, 2025
Downgraded Java to JDK 1.1 After 30 Years… (part 1)

How should we change Java 23 code for it to run on Java 1.1? We go line by line, removing modern features like records, sealed classes, switch expressions, var, and more. Each step reveals what breaks, how to rewrite it, and what you lose in the process. If you've ever wondered how far modern Java has drifted from its roots - this is your deep dive into that gap. This is Part 1 of the Java Downgrade Challenge, where we descend version by version until we reach Java 8. Subscribe to our channel to find out how we go even deeper - all the way down to Java 1.1. Stay tuned!

Videos
card image
Apr 25, 2025
Java in 2025: Busting the Biggest Myths

Think Java is slow, outdated, or only for old-school enterprise apps? Think again. In this episode of Java Myth Busters, we debunk six common myths about Java, including performance issues, security concerns, and the myth that Java is dead. Discover how the JVM, Spring Boot, GraalVM, and modern JDK updates make Java one of the most powerful, scalable, and relevant languages in 2025.

Further watching

Videos
card image
Jul 15, 2025
Java Downgrade Challenge: From JDK 8 to 1.1 (Part 2)

In Part 2 of the Java Downgrade Challenge, we continue our journey — now from Java 8 all the way to Java 1.1. No streams, no lambdas, no generics, no collections — and at one point, we even boot up Windows 98. If you thought Part 1 was painful, this one unwinds Java history line by line. By the end, the familiar Java from today will be almost gone.

Videos
card image
Jun 27, 2025
5x Smaller Java Docker Images — 2025 Optimization Guide

In this video, I’ll show you how to make your Java Docker images 5 TIMES SMALLER. You’ll see the full process, from a heavy 587MB container to a lean 116MB image, ready for production.

Videos
card image
Jun 23, 2025
How to install Liberica Native Image Kit on Windows PC

Liberica Native Image Kit is a multilingual GraalVM-based set of utilities for creating native images. This guide will help you to install it on Windows PC.