Real World Example

Different Views of the Same Product

Swiggy, Zomato and Flipkart show the same item in many places: a small card in search results, a full detail page, and an admin view for sellers with cost and margin. One entity, several DTOs:

DTOUsed byFields
ProductCardSearch/listing pageid, name, price, imageUrl, inStock
ProductDetailProduct pageall public fields + description, category
SellerProductViewSeller dashboarddetail + costPrice, margin, stock

ShopEase: Listing Cards vs Detail

public record ProductCard(Long id, String name, BigDecimal price, boolean inStock) {}

public record ProductDetail(Long id, String name, String description,
                            BigDecimal price, int stock, String category) {}

@RestController
@RequestMapping("/api/products")
public class ProductController {
    private final ProductService service;
    private final ProductMapper mapper;

    public ProductController(ProductService service, ProductMapper mapper) {
        this.service = service;
        this.mapper = mapper;
    }

    @GetMapping
    public List<ProductCard> list() {
        return service.findAll().stream().map(mapper::toCard).toList();
    }

    @GetMapping("/{id}")
    public ProductDetail get(@PathVariable Long id) {
        return mapper.toDetail(service.getById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public ProductDetail create(@RequestBody CreateProductRequest request) {
        return mapper.toDetail(service.create(mapper.toEntity(request)));
    }
}

Example

The listing is small and fast; the detail has everything the product page needs:

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

Example output

[{"id":1,"name":"Wireless Mouse","price":799.00,"inStock":true},{"id":2,"name":"Clean Code","price":450.00,"inStock":false}]
{"id":1,"name":"Wireless Mouse","description":"2.4 GHz, silent clicks","price":799.00,"stock":120,"category":"Electronics"}

Example

A client that tries to set the id or cost price is simply ignored, because those fields are not in the request DTO:

curl -X POST localhost:8080/api/products -H "Content-Type: application/json" -d '{"id":999,"costPrice":1,"name":"USB-C Cable","description":"1 m braided","price":299.00,"stock":500,"category":"Electronics"}'

Example output

{"id":3,"name":"USB-C Cable","description":"1 m braided","price":299.00,"stock":500,"category":"Electronics"}