Java

Lesson 04

Exception handling boundaries

Throw domain-specific exceptions inside the application and translate them at the outer boundary.

Good Code

src/main/java/dev/review/api/UserController.java
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();
        }
    }
}

Bad Code

src/main/java/UserController.java
public class UserController {
    public UserResponse show(String id) {
        try {
            return users.profileFor(id);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }
}

Review Notes

What to review

Good Code

The good version keeps the service failure meaningful, then maps it to HTTP at the controller boundary. The response shape stays predictable.

Bad Code

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.

Takeaways

  • Exceptions should carry useful meaning inside the system and become user-facing responses only at a clear boundary.