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
Jan 20, 2026
JDBC vs ORM vs jOOQ: Choose the Right Java Database Tool

Still unsure what is the difference between JPA, Hibernate, JDBC, or jOOQ and when to use which? This video clarifies the entire Java database access stack with real, production-oriented examples. We start at the foundation, which is JDBC, a low-level API every other tool eventually relies on for database communication. Then, we go through the ORM concept, JPA as a specification of ORM, Hibernate as the implementation and extension of JPA, and Blaze Persistence as a powerful upgrade to JPA Criteria API. From there, we take a different path with jOOQ: a database-first, SQL-centric approach that provides type-safe queries and catches many SQL errors at compile time instead of runtime. You’ll see when raw JDBC makes sense for small, focused services, when Hibernate fits CRUD-heavy domains, and when jOOQ excels at complex reporting and analytics. We discuss real performance pitfalls such as N+1 queries and lazy loading, and show practical combination strategies like “JPA for CRUD, jOOQ for reports.” The goal is to equip you with clarity so that you can make informed architectural decisions based on domain complexity, query patterns, and long-term maintainability.

Videos
card image
Jan 13, 2026
Hibernate: Ditch or Double Down? When ORM Isn't Enough

Every Java team debates Hibernate at some point: productivity champion or performance liability? Both are right. This video shows you when to rely on Hibernate's ORM magic and when to drop down to SQL. We walk through production scenarios: domain models with many-to-many relations where Hibernate excels, analytical reports with window functions where JDBC dominates, and hybrid architectures that use both in the same Spring Boot codebase. You'll see real code examples: the N+1 query trap that kills performance, complex window functions and anti-joins that Hibernate can't handle, equals/hashCode pitfalls with lazy loading, and practical two-level caching strategies. We also explore how Hibernate works under the hood—translating HQL to database-specific SQL dialects, managing sessions and transactions through JDBC, implementing JPA specifications. The strategic insight: modern applications need both ORM convenience for transactional business logic and SQL precision for data-intensive analytics. Use Hibernate for CRUD and relationship management. Use SQL where ORM abstractions leak or performance demands direct control.

Further watching

Videos
card image
Feb 6, 2026
Backend Developer Roadmap 2026: What You Need to Know

Backend complexity keeps growing, and frameworks can't keep up. In 2026, knowing React or Django isn't enough. You need fundamentals that hold up when systems break, traffic spikes, or your architecture gets rewritten for the third time.I've been building production systems for 15 years. This roadmap covers three areas that separate people who know frameworks from people who can actually architect backend systems: data, architecture, and infrastructure. This is about how to think, not what tools to install.

Videos
card image
Jan 29, 2026
JDBC Connection Pools in Microservices. Why They Break Down (and What to Do Instead)

In this livestream, Catherine is joined by Rogerio Robetti, the founder of Open J Proxy, to discuss why traditional JDBC connection pools break down when teams migrate to microservices, and what is a more efficient and reliable approach to organizing database access with microservice architecture.

Videos
card image
Jan 27, 2026
Sizing JDBC Connection Pools for Real Production Load

Many production outages start with connection pool exhaustion. Your app waits seconds for connections while queries take milliseconds; yet, most teams run default settings that collapse under load. This video shows how to configure connection pools that survive real production traffic: sizing based on database limits and thread counts, setting timeouts that prevent cascading failures, and implementing an open source database proxy Open J Proxy for centralized connection management with virtual connection handles, client-side load balancing, and slow query segregation. For senior Java developers, DevOps engineers, and architects who need database performance that holds under pressure.