20. DTOs & Architectural Boundary Decoupling

Request/Response DTOs & Mapping Pipelines

Master Request and Response DTO design, explicit entity mapping pipelines, and production CRUD API implementations.

Request/Response DTOs & Mapping Pipelines

To insulate the database persistence layer from public HTTP traffic, separate DTO classes must be constructed for incoming requests and outgoing responses.


1. Request DTO vs Response DTO Specifications

Request DTO (Client Input Contract)

Exposes ONLY the fields clients are permitted to supply during resource creation or update:

public class StudentRequestDto {
    private String name;
    private String email;
    private Integer age;

    // Getters and Setters (Notice: NO id, NO isDeleted, NO createdAt)
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}

Response DTO (Client Output Contract)

Exposes ONLY public fields intended for client consumption:

public class StudentResponseDto {
    private Long id;
    private String name;
    private String email;
    private Integer age;

    // Getters and Setters (Exposes generated id, hides internal audit fields)
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}

2. Explicit DTO Mapping Pipelines

Transformation between DTOs and Entities occurs at the Service Layer boundary:

mapToEntity Repository.save Saved Entity mapToResponseDto StudentRequestDto Student Entity MySQL Database Persisted Student Entity StudentResponseDto

Mapping Implementation in Service Layer

@Service
public class StudentService {

    private final StudentRepository repository;

    public StudentService(StudentRepository repository) {
        this.repository = repository;
    }

    public StudentResponseDto createStudent(StudentRequestDto requestDto) {
        // Step 1: Map Request DTO ──► Entity
        Student student = mapToEntity(requestDto);

        // Step 2: Persist Entity
        Student savedStudent = repository.save(student);

        // Step 3: Map Entity ──► Response DTO
        return mapToResponseDto(savedStudent);
    }

    private Student mapToEntity(StudentRequestDto dto) {
        Student student = new Student();
        student.setName(dto.getName());
        student.setEmail(dto.getEmail());
        student.setAge(dto.getAge());
        // Intentionally leaves id, isDeleted, createdAt to server control!
        return student;
    }

    private StudentResponseDto mapToResponseDto(Student entity) {
        StudentResponseDto dto = new StudentResponseDto();
        dto.setId(entity.getId());
        dto.setName(entity.getName());
        dto.setEmail(entity.getEmail());
        dto.getAge(entity.getAge());
        return dto;
    }
}

3. CRUD API Implementation with DTOs

@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final StudentService service;

    public StudentController(StudentService service) {
        this.service = service;
    }

    @PostMapping
    public StudentResponseDto createStudent(@Valid @RequestBody StudentRequestDto requestDto) {
        return service.createStudent(requestDto);
    }

    @GetMapping("/{id}")
    public StudentResponseDto getStudentById(@PathVariable Long id) {
        return service.getStudentById(id);
    }

    @GetMapping
    public List<StudentResponseDto> getAllStudents() {
        return service.getAllStudents();
    }

    @PutMapping("/{id}")
    public StudentResponseDto updateStudent(@PathVariable Long id, @Valid @RequestBody StudentRequestDto requestDto) {
        return service.updateStudent(id, requestDto);
    }
}

❓ Knowledge Check

Knowledge Check

Where should DTO-to-Entity mapping take place in a layered Spring Boot application?

Knowledge Check

Why does updateStudent retrieve the existing Entity before applying DTO fields?

On this page