Records as DTOs

Request and Response Records

Java records are ideal DTOs: immutable, short, with equals, hashCode and toString for free. Jackson reads and writes them without extra annotations.

package com.shopease.product.dto;

import java.math.BigDecimal;

// What the client sends to create a product: no id, no audit fields
public record CreateProductRequest(String name, String description,
                                   BigDecimal price, int stock, String category) {}

// What the client receives
public record ProductResponse(Long id, String name, String description,
                              BigDecimal price, int stock, String category,
                              boolean inStock) {}

Nested DTOs and Custom JSON Names

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonInclude(JsonInclude.Include.NON_NULL)          // skip null fields in the JSON
public record ProductSummary(
        Long id,
        String name,
        @JsonProperty("mrp") BigDecimal price,      // JSON name differs from Java name
        CategoryRef category) {

    public record CategoryRef(Long id, String name) {}
}

Example

curl localhost:8080/api/products/1/summary

Example output

{"id":1,"name":"Wireless Mouse","mrp":799.00,"category":{"id":4,"name":"Electronics"}}

Note: Jackson uses camelCase names by default. To use snake_case for the whole API set spring.jackson.property-naming-strategy=SNAKE_CASE.