Edward Harker

UseCases should have one public method

The domain layer of an app is where the business logic lives. You might call these classes UseCases, Services or something else. Whatever you call them, they should have one public method.

class UserUseCase(
    private val userRepository: UserRepository,
    private val passwordValidator: PasswordValidator
) {
    fun login(username: String, password: String) {}
    fun logout() {}
}

In this example, we have multiple public methods for actions that our app could make on a user. This is bad because:

  • We’re breaking the single responsibility principle
  • Therefore, the class is more complex because it’s doing many things
  • This makes it harder to test because our class is more complex
  • Finally, we’re going to be injecting dependencies that aren’t required for all methods

Instead let’s have two distinct classes.

class LoginUseCase(
    private val userRepository: UserRepository,
    private val passwordValidator: PasswordValidator
) {
    fun login(username: String, password: String) {}
}

class LogoutUseCase(
  	private val userRepository: UserRepository,
) {
    fun logout() {}
}
  • Each class is responsible for one piece of business logic and is more likely to be following the single responsibility principle
  • Now, we only need to inject the required dependencies. For example, the LogoutUseCase doesn’t need the passwordValidator
  • Each UseCase will be less complex and therefore easier to test

Following this rule also benefits code organisation and discoverability of UseCases in a large codebase. It’s easy for a piece of business logic to be buried in a large class with many public methods. If you follow this rule you can search in the IDE for classes or browse them in the file tree. This is much harder when you need to first think of the class and then look for the method inside the class.