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 🚀