Implementing Data Transfer Objects

1. Creating Request DTOs

TraitDetail
PurposeCarry input from API client
NamingCreate*Request, Update*Request
ValidationBean Validation annotations
No domain logicPure data carrier

Example: Request record with validation

public record CreateOrderRequest(
    @NotNull UUID customerId,
    @NotEmpty @Valid List<LineRequest> lines,
    @Pattern(regexp="^[A-Z]{3}$") String currency
) {}

2. Creating Response DTOs

AspectGuideline
Stable contractDon't expose entity fields directly
Include only neededAvoid leaking internals
Format datesISO-8601 strings
IDsString or UUID, never internal long

3. Implementing DTO Mapping

ApproachProsCons
ManualExplicit, debuggableBoilerplate
MapStructCompile-time, fastAnnotation processor
ModelMapperReflection conventionSlow, magical
Jackson convertersInlineLimited

4. Designing Nested DTOs

Example: Nested response

public record OrderResponse(
    String id,
    CustomerSummary customer,
    List<LineResponse> lines,
    MoneyResponse total) {
    public record CustomerSummary(String id, String name) {}
    public record LineResponse(String productId, int qty, MoneyResponse price) {}
    public record MoneyResponse(long cents, String currency) {}
}

5. Using Validation Annotations

AnnotationPurpose
@NotNullReference must be non-null
@NotBlankString non-null and non-empty trimmed
@NotEmptyCollection/string non-empty
@Size(min,max)Length range
@Min/@MaxNumeric range
@PatternRegex
@EmailEmail format
@ValidCascade validation
@Past/@FutureTemporal

6. Implementing Pagination DTOs

Example: Page response

public record PageResponse<T>(
    List<T> items,
    int page, int size,
    long totalElements, int totalPages,
    boolean hasNext, boolean hasPrev) {}

7. Implementing Filter DTOs

Example: Search filter

public record OrderFilter(
    @Nullable String status,
    @Nullable Instant placedAfter,
    @Nullable Instant placedBefore,
    @Nullable Long minTotalCents) {}

8. Understanding DTOs vs Domain Models

AspectDTODomain
PurposeData transportBusiness behavior
ValidationFormat/syntaxInvariants
LifetimePer requestLong-lived
CouplingExternal contractInternal

9. Implementing Versioned DTOs

StrategyDetail
Package per versionv1.OrderResponse, v2.OrderResponse
Mapper per versionDomain → versioned DTO
AvoidAdding optional fields without versioning

10. Handling Null Values

ApproachDetail
Jackson @JsonInclude(NON_NULL)Omit null fields
Default valuesEmpty list/map instead of null
OptionalAvoid in DTOs (Jackson quirks)
Tri-stateJsonNullable for absent vs explicit null

11. Implementing Command DTOs

TraitDetail
IntentImperative; "do this"
NamingPlaceOrderCommand
CarriesInputs needed by handler
CQRS fitWrite side

12. Implementing Query DTOs

TraitDetail
IntentInterrogative; "give me"
NamingFindOrdersQuery
No state changePure read
CQRS fitRead side