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.

Summary

In this video, the idea of whether DTOs are always necessary in Spring Boot applications is challenged. Instead of generating multiple DTO classes and mappings, it shows how Jackson annotations such as @JsonIgnore and @JsonView, or Mixins, can manage serialization and control which fields are exposed. This reduces boilerplate, keeps entities simpler, and improves maintainability. The takeaway is that while DTOs have their place, in many cases they can be avoided with these alternatives.

Social Media

Videos
card image
Dec 12, 2025
Will AI Replace Developers? A Vibe Coding Reality Check 2025

Can AI replace software engineers? ChatGPT, Copilot, and LLM-powered vibe coding tools promise to automate development—but after testing them against 17 years of production experience, the answer is more nuanced than the hype suggests. Full project generation produces over-engineered code that's hard to refactor. AI assistants excel at boilerplate but fail at business logic. MCP servers solve hallucination problems but create context overload. Meanwhile, DevOps automation actually works. This breakdown separates AI capabilities from marketing promises—essential for teams integrating LLMs and copilots without compromising code quality or architectural decisions.

Videos
card image
Dec 12, 2025
JRush | Container Essentials: Fast Builds, Secure Images, Zero Vulnerabilities

Web-conference for Java developers focused on hands-on strategies for building high-performance containers, eliminating CVEs, and detecting security issues before production.

Further watching

Videos
card image
Dec 30, 2025
Java in 2025: LTS Release, AI on JVM, Framework Modernization

Java in 2025 isn't about headline features, it's about how production systems changed under the hood. While release notes focus on individual JEPs, the real story is how the platform, frameworks, and tooling evolved to improve stability, performance, and long-term maintainability. In this video, we look at Java from a production perspective. What does Java 25 LTS mean for teams planning to upgrade? How are memory efficiency, startup time, and observability getting better? Why do changes like Scoped Values and AOT optimizations matter beyond benchmarks? We also cover the broader ecosystem: Spring Boot 4 and Framework 7, AI on the JVM with Spring AI and LangChain4j, Kotlin's growing role in backend systems, and tooling updates that make upgrades easier. Finally, we touch on container hardening and why runtime and supply-chain decisions matter just as much as language features.

Videos
card image
Dec 24, 2025
I Solved Advent of Code 2025 in Kotlin: Here's How It Went

Every year, Advent of Code spawns thousands of solutions — but few engineers step back to see the bigger picture. This is a complete walkthrough of all 12 days from 2025, focused on engineering patterns rather than puzzle statements. We cover scalable techniques: interval math without brute force, dynamic programming, graph algorithms (JGraphT), geometry with Java AWT Polygon, and optimization problems that need constraint solvers like ojAlgo. You'll see how Java and Kotlin handle real constraints, how visualizations validate assumptions, and when to reach for libraries instead of writing everything from scratch. If you love puzzles, programming—or both—and maybe want to learn how to solve them on the JVM, this is for you.

Videos
card image
Dec 18, 2025
Java 26 Preview: New JEPs and What They Mean for You

Java 26 is the next feature release that brings features for enhanced performance, security, and developer experience. This video discusses the upcoming JDK 26 release, highlighting ten JEPs including JEP 500. JEP 500 focuses on preparing developers for future restrictions on mutating final fields in Java, emphasizing their role in maintaining immutable state. This is crucial for robust programming and understanding the nuances of mutable vs immutable data, especially concerning an immutable class in java. We also touch upon the broader implications for functional programming in Java.