Stop Using DTOs – A Cleaner Way for Your Java APIs
Transcript:
You don't need details before I close the video. Hear me out. Maybe just maybe you or your neighbor or you colleague need them, but chances are you are doing it wrong. What I mean, probably you know how to use DTO's. Probably you test them, but do know that you actually usually can avoid using them.
What is the history of using? Actually they originate from somewhere like 2005 when the whole idea of returning entities was very scary because when you returns an entity, you have to serialize it right wrong. JsonView, JsonIgnore and other useful notations can help you avoid it. And today we'll talk about them. Hi, you are on CyberJAR I'm Pasha. I'm developer advocated BellSoft, and I'm more than 15 years in software development in JVM. And every time I hear about Detailss, it makes me think, is everything going right here? Let's discuss.
If you follow our channel, then you just saw a video by my colleague Kat. About how to use DTO with Spring Boot and in Spring Boot applications. She describes a lot of useful applications of DTO, validations and custom beans and so on, but think about the amount of machinery, additional machinery we should use. Just to use this approach, we have to create a lot of DTO classes, potentially a lot because for one entity we can have almost infinite amount of projections, and all these projections will have the same names of fields as our entity. Most probably there are exceptions. Surely. Also, we have to rely on co-generation. I personally love co generation. But do I really want to map everything with generated code, which is simple, but it's still a tone of code and it's not like I really control it, right? When I control my code generation, I'm totally fine with it, but when someone else does it, it come out of hands very quickly. Is it fine by you? Do you like it? I think that I know a better approach. Let's start from a simple example. I have a very basic entity here on your screen. It's just a user with username and password, and it's so simple to imagine a situation when there are a little bit more fields and we want to return them all, but password. And many of you probably reflectively created DTO for it, like user without password DTO, and then you'll map a user entity to user without password DTO with MapStruct or tool like that, or just manually and return it just to be on the safe side. But it is quite a boilerplate to be honest. We know that we certainly never want to return passages, right?
Maybe we can just put json ignore annotation on this field. It reduces the amount of code we have to write dramatically. It's one line of code, actually it's meta code. It's just an notation. And I would argue this code is actually more maintainable than the version of DTO's. It, we have only one entity for everything and it doesn't leak unnecessary information to our users. So we are safe and our code is shorter and easier to read. I hear your voices and that too loud. Pasha. This example is oversimplified. Okay. Okay. You are right. Let's get back for a second. Let's imagine that there is one more field in our entity. It's email and we do not want to return email to regular users of our API or front end, but we want to return email to administrators or say internal personal of our company. Surely. You know how to do it with dts, you will just create two more dts. One of them is use a DTO without email and without password and use a DTO without password with email. Two more. Okay, that's fine. No it's not. You could just use JsonView annotation. Let's see how it works. Look at this class views. It only contains two class inside of it, public and internal extends public. All the fields elevated with json View public should be available to our general audience. All the fields annotated with JsonView internal as well as all the field sanitation JSON view public should be available to our administrators or internal personnel. Now let's update our entity a little bit. We'll add JSON view public to our IT and username. They could be available to anyone. We'll update email to be JSON View internal, and we'll leave password as is json Ignore. Now let's update our controller. Because it is the place actually responsible for realization with json Ignore it's symbol. When Jackson sees a json ignore field, it'll just ignore it. But views, we have to actually update our controllers. Here is how we do it. We just add json, view annotation on methods of our controls, like fixed. What are this voices again? Pasha, I don't use JPA. Oh, you use JUG Actually, I'm on board with you. I don't like JPA either. JUG gives me much more control over my SQL and I love it. But there is an issue, right? JUG generates code. We can't just put annotations on generated code. Do we actually need them? Is devoid, do we need to use ju generated DTO? Actually, we don't. To address the hq, Jackson uses an approach very popular in say, scatter world, but almost unknown in Java world and it's mixings.
Let's look at the mixing exam. Mixin is just an interface and it only contains getters of our fields. And this getters can be annotated by Jackson notations like JsonView or JsonIgnore. For example. Here we see two ghettos annotated with JsonView public and one ghetto annotated with JSON. No. When we create such mixing, the only thing we left to do is to register this mixing in Jackson. Here's how we do it. Obviously, you are not obligated to apply Mixings only to records. If you want, you can apply them to JPA entities or any other classes you don't control. It's totally in your power. The beautiful thing here, your class shouldn't implement the interface of mixing. The mixing is just a interface known only to Jackson Register only for Jackson, which controls the serialization behavior. What is the sound again? I'm hearing. I'm hearing, I'm hearing your question. Pasha, our objects are not as simple as you show. We have complex data structures and they are in JPE entities. They depend on each other, and when I have a comment, this comment has a user. Our users do not exist in vacuum. They're bound to something real amazing news for you.
If your person has a list of comments or if your comment has a link to user. You can annotate fields in all of them with your views and the whole structure will be visible only to a degree that is allowed by JsonView annotation, like here an example. Now I know there is a couple more questions. First, validation. Beautiful. You can put validation annotations. On your entities too, and if you need to validate it, you will. And also, Hibernate Validator supports validation types of validation classes, so you are not obligated to validate all the fields. You can validate some of them, depending on your context, on your situation. The second, many of you think, or many of you will say that dts are more explicit than annotations? Well, kinda, but not. Annotations are also explicit and what's more important, they are reusable. They do not care about how many projections you have.
You will always have what you need without excessive amount of classes. And when one day you'll have to move field from one view to another, you will just do it and it won't break any API. Sure you'll have to fix your test, but this is what we have tests for, right? So, so what do we take out from this video? I hope that now you at least have another view. On the question, do I need a DTO? If you need it, please use it. Just know that there is an alternative. If you wanna know more about dts, please check out Kat's video. If you like the video, like the video. If you don't like the video, please leave a comment. Tell us what you think, and maybe I'll fix it. Who knows? And with this patches out, see you next time.