I still don't get how Java records can be used for anything like a DTO. Since you're a Clojure dev you may remember the pattern Rich Hickey described as "place oriented programming" :) Nearly every endpoint will have more than 2-3 fields and you really don't want a Java record with more than that many fields for the same reason you don't want a Java method with that many fields e.g. doIt(Long, String,String,Long,String,int,int,String) <-- code smell.
And the problem I always see is something may start off as a Java record and then need to be refactored into a class as soon as 1-2 more fields are added.
> I still don't get how Java records can be used for anything like a DTO
In Clojure, we often deserialize a result set from a database to a vector of maps. These maps have different keys depending on what exactly your query was selecting. In Java, one often "projects" results to some DTO. This is one scenario where records offer identical functionality while avoiding boilerplate.
Regarding "place-oriented programming", records are immutable, so that is one technical advantage they have over handwriting a DTO. And from my short experience using web frameworks like Quarkus, it seems that a lot of the "design patterns" I see in documentation exist to help design easy-to-test programs rather than unfettered mutation.
Additionally, I have found records useful for describing the payload of endpoints that accept map-like data. Without records, I would be writing POJOs with public fields anyway.
Overall, Java records behave like TypeScript interfaces with awkward syntax. I have found them ideal for expressing type-safe, map-like data with minimal boilerplate.
I'm also using it for projections. But to be honest we have a fairly large Quarkus app and only use projections in a few places. For immutable classes with a large number of fields where instances have to be created manually I usually use the builder pattern. But the analogy to typescript interfaces is interesting.
And the problem I always see is something may start off as a Java record and then need to be refactored into a class as soon as 1-2 more fields are added.