Let me tell you about the time I debugged a weird bug for half a day… only to discover a null into the database because someone thought that the first name isn’t mandatory.
Yeah, not my proudest moment. But it taught me something important:
Always validate your data before it hits the database. Not after. Not during. Before.
This might sound obvious, but I still see a lot of Java applications treating validation as an afterthought. The result? Spaghetti logic, surprise NullPointerExceptions, and worst of all inconsistent data that makes everything harder.
Let’s fix that.
Early Validation
If you validate all incoming data right when it enters the system, in the controller or a service layer, a few magical things happen:
- Your database stays clean and predictable.
- You avoid littering your codebase with
if (data != null && ...). - Your business logic gets way simpler
- Bugs become easier to spot and fix.
It’s the classic “garbage in, garbage out” problem. Don’t let garbage in.
Controllers Must Validate Input Data
Think of your app like a security building. You wouldn’t wait for sneaky hackers to stroll in, kick off their shoes, and ask for a snack before checking their ID; no, you’d be stopping them at the entrance.
In a typical Spring Boot application, the “gates” are your controllers or entry-point services. Use validation annotations to keep things tight:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<Void> createUser(@Valid @RequestBody UserDto userDto) {
userService.create(userDto);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
public class UserDto {
@NotBlank
private String username;
@Email
private String email;
@Size(min = 8)
private String password;
// getters and setters
}
Spring will automatically reject invalid input with a 400 Bad Request. Clean, declarative, no if statements in sight.
Validate Your Business Data
One validation step is good. Two is better.
Validate at the DTO level when data enters the system. Then validate again when it moves into your domain model — especially if you’re using complex business rules.
For example:
public class User {
private final String username;
private final String email;
public User(String username, String email) {
if (username == null || username.isBlank()) {
throw new IllegalArgumentException("Username is required");
}
if (!email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
this.username = username;
this.email = email;
}
// getters
}
Yes, it’s a bit more work. But it gives you two lines of defense:
- External validation (catching bad data from users or the network)
- Internal validation (making sure your domain always behaves correctly)
Avoiding Checking for Null Values
The real win here? Once you validate your inputs up front, you don’t need to wrap every piece of business logic in paranoid if-else blocks.
// Without validation
if (user != null && user.getEmail() != null && user.getEmail().contains("@")) {
sendWelcomeEmail(user);
}
// With validation
sendWelcomeEmail(user); // Boom. Clean.
Suddenly your code looks like it was written by someone who enjoys life.
Bonus: Custom Validators for the Win
Sometimes built-in annotations aren’t enough. That’s fine, just write your own:
@Documented
@Constraint(validatedBy = CountryCodeValidator.class)
@Target({ FIELD })
@Retention(RUNTIME)
public @interface ValidCountryCode {
String message() default "Invalid country code";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class CountryCodeValidator implements ConstraintValidator<ValidCountryCode, String> {
@Override
public boolean isValid(String code, ConstraintValidatorContext context) {
return List.of("US", "AR", "DE").contains(code);
}
}
Plug that into your DTO and you’re golden.
Validation Checklist
- Validate input as early as possible, preferably at the DTO/controller level.
- Don’t trust data from the outside world (or the inside world, honestly).
- Add internal checks in your domain models if needed.
- Use annotations and custom validators to keep things declarative.
- Simplify logic by avoiding repetitive null and condition checks.
Clean validation = cleaner data = cleaner code. Your future self (and your team) will thank you.



Leave a comment