Java

Lesson 10

Package and service boundaries

Keep controllers, services, repositories, and packages focused so each layer owns one kind of decision.

Good Code

src/main/java/dev/review/user/UserService.java
package dev.review.user;

public final class UserService {
    private final UserRepository users;
    private final WelcomeEmail welcomeEmail;

    public UserService(UserRepository users, WelcomeEmail welcomeEmail) {
        this.users = users;
        this.welcomeEmail = welcomeEmail;
    }

    public UserProfile register(CreateUserCommand command) {
        User user = users.save(User.create(command.email(), command.displayName()));
        welcomeEmail.sendTo(user);
        return UserProfile.from(user);
    }
}

Bad Code

src/main/java/UserController.java
public class UserController {
    public ResponseEntity<?> register(HttpServletRequest request) throws SQLException {
        String email = request.getParameter("email");
        Connection connection = DriverManager.getConnection(databaseUrl);
        Statement statement = connection.createStatement();
        statement.executeUpdate("insert into users(email) values('" + email + "')");
        mailer.send(email, "Welcome");
        return ResponseEntity.ok().build();
    }
}

Review Notes

What to review

Good Code

The good version gives business behavior a service boundary. Persistence and email are dependencies, not hidden work inside a controller.

Bad Code

The bad version mixes request parsing, SQL construction, database connection management, email, and HTTP response code in one class.

Takeaways

  • A Java class is easier to review when HTTP, business rules, and persistence are separated by clear package boundaries.