Good Code
The good version keeps the service failure meaningful, then maps it to HTTP at the controller boundary. The response shape stays predictable.
Lesson 04
Throw domain-specific exceptions inside the application and translate them at the outer boundary.
public final class UserController {
private final UserService users;
public ResponseEntity<UserResponse> show(UUID id) {
try {
UserProfile profile = users.profileFor(new UserId(id));
return ResponseEntity.ok(UserResponse.from(profile));
} catch (UserNotFoundException exception) {
return ResponseEntity.notFound().build();
}
}
}public class UserController {
public UserResponse show(String id) {
try {
return users.profileFor(id);
} catch (Exception exception) {
exception.printStackTrace();
return null;
}
}
}The good version keeps the service failure meaningful, then maps it to HTTP at the controller boundary. The response shape stays predictable.
The bad version catches every exception, prints diagnostics to standard output, and returns null. The caller now has a second failure mode that hides the original problem.