Real World Example

DTOs for an Order API

In a Spring Boot backend, request and response bodies are perfect records: they are just data moving between layers. Here a service turns a request record into a response record with a computed total.

Example

import java.util.List;

public class Main {
    record OrderItemRequest(String productId, int quantity, long unitPricePaise) {
        OrderItemRequest {
            if (quantity <= 0) throw new IllegalArgumentException("quantity must be > 0 for " + productId);
        }
        long lineTotal() { return quantity * unitPricePaise; }
    }

    record CreateOrderRequest(String customerId, List<OrderItemRequest> items) {
        CreateOrderRequest {
            items = List.copyOf(items);              // defensive copy: truly immutable
        }
    }

    record OrderResponse(String orderId, String customerId, int itemCount, String total) { }

    static int nextId = 1000;

    static OrderResponse create(CreateOrderRequest req) {
        long total = 0;
        for (OrderItemRequest item : req.items()) total += item.lineTotal();
        return new OrderResponse("ORD-" + (++nextId), req.customerId(), req.items().size(),
                String.format("Rs %d.%02d", total / 100, total % 100));
    }

    public static void main(String[] args) {
        CreateOrderRequest req = new CreateOrderRequest("CUST-7", List.of(
                new OrderItemRequest("P-101", 2, 49900),
                new OrderItemRequest("P-205", 1, 129950)));
        System.out.println(create(req));

        try {
            new OrderItemRequest("P-300", 0, 1000);
        } catch (IllegalArgumentException e) {
            System.out.println("400 Bad Request: " + e.getMessage());
        }
    }
}

Output

OrderResponse[orderId=ORD-1001, customerId=CUST-7, itemCount=2, total=Rs 2297.50]
400 Bad Request: quantity must be > 0 for P-300