Spring Boot

Capstone – Article 4: DTOs, Validation & API Contracts (Expense Tracker)

By Utility Zone · 2026-01-27T18:31:56.681343

1. Introduction

So far, our APIs work — but they are not production-ready.

Problems with current approach:

  • Exposing entity structure directly
  • No input validation
  • Weak API contracts

In this article, we will:

  • Introduce DTOs
  • Add request validation
  • Clean up API boundaries

This is a professional backend practice.


2. Why DTOs Are Important

DTO (Data Transfer Object):

  • Represents API input/output
  • Decouples API from database entities
  • Improves security and flexibility

❌ Do NOT expose entities directly from controllers
✔ Use DTOs at API boundaries


3. Creating DTO Package

Create package:

com.example.expensetracker.dto

4. User Request DTO

4.1 Create UserRequest

package com.example.expensetracker.dto;

import jakarta.validation.constraints.*;

public class UserRequest {

    @NotBlank(message = "Name is mandatory")
    private String name;

    @Email(message = "Email must be valid")
    @NotBlank(message = "Email is mandatory")
    private String email;

    @NotBlank(message = "Password is mandatory")
    @Size(min = 6, message = "Password must be at least 6 characters")
    private String password;

    @NotBlank(message = "Role is mandatory")
    private String role;

    // getters and setters
}

5. User Response DTO

5.1 Create UserResponse

package com.example.expensetracker.dto;

public class UserResponse {

    private Long id;
    private String name;
    private String email;
    private String role;

    // getters and setters
}

Note:

  • Password is intentionally excluded
  • API responses are safer

6. Mapping DTOs to Entity

6.1 Manual Mapping (Recommended for Learning)

private User mapToEntity(UserRequest request) {
    User user = new User();
    user.setName(request.getName());
    user.setEmail(request.getEmail());
    user.setPassword(request.getPassword());
    user.setRole(request.getRole());
    return user;
}

private UserResponse mapToResponse(User user) {
    UserResponse response = new UserResponse();
    response.setId(user.getId());
    response.setName(user.getName());
    response.setEmail(user.getEmail());
    response.setRole(user.getRole());
    return response;
}

Mapping libraries (MapStruct) can be added later.


7. Updating UserService

public UserResponse createUser(UserRequest request) {
    User user = mapToEntity(request);
    User savedUser = userRepository.save(user);
    return mapToResponse(savedUser);
}

Service now controls mapping logic.


8. Updating UserController

@PostMapping
public UserResponse createUser(@Valid @RequestBody UserRequest request) {
    return userService.createUser(request);
}

✔ @Valid triggers validation
✔ Clean request/response models


9. Validation in Action

Send invalid request:

{
  "name": "",
  "email": "invalid",
  "password": "123",
  "role": ""
}

Spring automatically returns:

  • HTTP 400
  • Validation error messages

No extra code required.


10. API Contract Benefits

With DTOs: ✔ Entities can change without breaking API
✔ Validation rules are clear
✔ Security improves
✔ Frontend gets predictable responses


11. Common Beginner Mistakes

❌ Validating entities
❌ Returning entity with password
❌ Skipping DTOs for speed
❌ Mixing validation logic


12. Git Commit (Important)

Commit your changes:

git add .
git commit -m "Introduce DTOs and validation for User APIs"

13. What You Should Have Now

At this point:

  • Clean API contracts
  • Validation enforced
  • Safer responses
  • Professional structure

Your backend is now API-quality.


14. What’s Next?

➡ Capstone – Article 5: Global Exception Handling

  • Custom exceptions
  • @ControllerAdvice
  • Standard error responses

Type Next when you’re ready 🚀