Browse Java Design Patterns & Enterprise Application Architecture

Implementing DTO in Java

Implement DTOs in Java with boundary-specific fields, simple records or classes, and explicit mapping from domain or persistence models.

DTO (Data Transfer Object): A simple object whose job is to carry data across a boundary without bringing business behavior or internal model details with it.

In Java, a DTO is useful when the shape required by an API, message, or remote call is not the same shape the domain model should expose internally. That is the core reason to implement one. Not “because layers need their own classes” by default.

Start With The Boundary

The first implementation decision is not whether the DTO should be a class or record. It is what boundary the DTO belongs to:

  • REST response
  • request payload
  • message contract
  • batch export row
  • integration client payload

Once that boundary is clear, the field set becomes much easier to justify.

A Simple Record-Based DTO

For many Java DTOs, a record is a strong default:

1public record CustomerSummaryDto(
2    String id,
3    String displayName,
4    String tier,
5    boolean active
6) {}

This works well because DTOs are usually:

  • data-only
  • easy to serialize
  • easy to compare in tests
  • not intended to own business behavior

Records are not mandatory, but they often match the purpose of a DTO very well.

Keep DTOs Different From Domain Models On Purpose

Suppose the domain model contains:

  • validation logic
  • value objects
  • internal invariants
  • persistence-only fields

The DTO does not need to mirror all of that.

 1public final class Customer {
 2    private final CustomerId id;
 3    private final FullName name;
 4    private final LoyaltyTier tier;
 5    private final CreditProfile creditProfile;
 6    private final AuditMetadata auditMetadata;
 7
 8    // domain behavior omitted
 9}
10
11public record CustomerSummaryDto(
12    String id,
13    String displayName,
14    String tier
15) {}

That difference is the point. The DTO carries the boundary-relevant view of the data, not the full internal model.

Mapping Should Stay Explicit

Even when mapping is later automated, the boundary rule should be understandable from explicit code:

1public final class CustomerDtoMapper {
2    public CustomerSummaryDto toSummaryDto(Customer customer) {
3        return new CustomerSummaryDto(
4            customer.id().value(),
5            customer.name().formatted(),
6            customer.tier().name()
7        );
8    }
9}

This makes three important things visible:

  • which fields cross the boundary
  • which formatting decisions happen at the boundary
  • which internal details are intentionally not exposed

What DTOs Should Not Become

DTOs become weak when they turn into one of these:

  • domain objects with behavior removed mechanically
  • persistence entities reused as transport payloads
  • giant “everything response” bags for convenience
  • objects carrying validation, workflow, or business rules that belong elsewhere

If the DTO is just a copy of the entity plus public setters, the design probably needs another pass.

Serialization Is A Concern, Not The Purpose

DTOs are often serialized to JSON, XML, or message formats, but serialization is not the pattern’s reason to exist. The real purpose is owning a boundary shape deliberately. Serialization is just one consequence.

Design Review Questions

When reviewing a Java DTO, ask:

  • Which boundary does this DTO belong to?
  • Why is this field included and why is that one excluded?
  • Is the DTO intentionally shaped, or just copied from an entity?
  • Is mapping owned in a clear place?

DTO is strong when it clarifies the boundary contract. It is weak when it exists only because “layered architecture” was copied from an old diagram.

Loading quiz…
Revised on Thursday, April 23, 2026