Проблема UseCase-ов: что нужно знать разработчикам Android

Введение

Если вы уже определенное время занимаетесь разработкой Android, вы, вероятно, слышали о UseCases. Их часто представляют как Святой Грааль Clean architecture. UseCases призваны отделить бизнес-логику от Presentation и Data слоев, сделав ваш код более модульным, переиспользуемым и тестируемым. Но вот в чем загвоздка: UseCases не всегда являются серебряной пулей. На самом деле, слепое их применение может привести к раздутому коду и ненужной сложности, чего как раз и пытается избежать Clean Architecture. В этой статье мы развенчаем миф о UseCases и обсудим, когда они необходимы, а когда — просто пустая трата времени. Если вы разработчик Android и задаетесь вопросом, приносите ли вы больше вреда, чем пользы, следуя этому шаблону, эта статья для вас.

b453576f661d9bbf69138a3590b5825f.jpg

Спорная правда о UseCases

Теоретически UseCases имеют смысл в Clean Architecture: они инкапсулируют бизнес-логику и гарантируют, что слои вашего приложения остаются разъединенными. Однако реальность в повседневной разработке приложений гораздо более тонкая.

Слишком много разработчиков относятся к UseCases как к требованию, что приводит к избыточному и ненужному коду. Результат? Вместо того чтобы сделать свои приложения более удобными для поддержки и масштабируемыми, они создают раздутые кодовые базы, заполненные слоями абстракции, которые не служат реальной цели.

29a1ec034e2cb73e35d779325af61e1e.jpg

Когда следует избегать UseCases: будь проще

Давайте начнем с того, когда следует избегать UseCases. Самая большая ошибка, которую допускают многие разработчики, — это использование UseCases для каждого небольшого действия в приложении, даже если не задействована значительная бизнес-логика.

Пример: Получение данных из Repository

Представьте, что вы создаете простое приложение для списка дел, в котором вам нужно получить список задач из локальной базы данных. Действие простое: вы запрашиваете Repository, получаете список и отображаете его в пользовательском интерфейсе.

Использование UseCase здесь только добавит ненужной сложности. У вас нет сложной бизнес-логики, которую нужно изолировать или повторно использовать в разных частях приложения. ViewModel может напрямую вызывать репозиторий без привлечения UseCase.

class MyViewModel(private val repository: TaskRepository) : ViewModel() {
    val tasks = liveData {
        emit(repository.getTasks())
    }
}

Почему здесь избегают UseCase? Потому что он не добавляет ценности. Логика уже проста, и введение UseCase только раздует код без какой-либо очевидной выгоды. Clean Architecture выступает за простоту и ясность, а здесь UseCase сделает обратное.

d1b76a4524b6afc49957214ac01b132e.jpg

Когда необходим UseCase: обработка сложной бизнес-логики

Теперь давайте поговорим о том, когда UseCase действительно необходим. UseCases стоит использовать, когда вам нужно инкапсулировать сложную бизнес-логику, которую нужно отделить от Presentation и Data слоев.

Пример: бронирование рейса в приложении для путешествий

Рассмотрим сценарий, в котором пользователь бронирует рейс в туристическом приложении. Этот процесс включает несколько шагов:

Проверка введенных пользователем данных (даты, пункты назначения).

  • Проверка доступности рейса

  • Бронирование рейса

  • Обработка платежа

  • Отправка подтверждения бронирования

Здесь вы имеете дело с несколькими UseCases между различными службами — доступность рейса, бронирование и оплата — каждое из которых может выдать ошибку.

UseCase в этом сценарии имеет смысл. Инкапсулируя процесс бронирования в UseCase, вы можете:

  • Обеспечить соблюдение всех бизнес-правил

  • Повторно использовать логику в разных частях приложения (например, бронирование через разные UI)

  • Упростить тестирование логики бронирования независимо от UI и уровней данных

class BookFlightUseCase(
    private val flightRepository: FlightRepository,
    private val paymentProcessor: PaymentProcessor
) {
    suspend operator fun invoke(flightId: String, userDetails: User, paymentInfo: PaymentInfo): BookingResult {
        if (!validateInput(userDetails)) throw InvalidInputException()
        val availability = flightRepository.checkAvailability(flightId)
        if (!availability) throw FlightNotAvailableException()

        val reservation = flightRepository.reserveFlight(flightId, userDetails)
        val paymentResult = paymentProcessor.processPayment(paymentInfo)

        return BookingResult.Success(reservation, paymentResult)
    }
}

В этом случае UseCase улучшает организацию кода, тестируемость и повторное использование. Без UseCase эта логика, скорее всего, окажется в ViewModel или Activity, что усложнит ее поддержку, тестирование и повторное использование.

Настоящее предназначение UseCases: избегание раздувания, а не его создание

Цель Clean Architecture — не создавать больше слоев абстракции ради самого процесса, а поддерживать вашу кодовую базу чистой, организованной и простой в обслуживании. Введение UseCases там, где это не нужно, нарушает этот принцип.

Вот где ошибаются многие разработчики: они следуют шаблонам, таким как UseCases, потому что им говорят, что это часть Clean Architecture, не понимая полностью, зачем они это используют. В результате их код становится загроможденным UseCases, которые не служат никакой реальной цели, превращая Clean Architecture в то, чего она должна предотвращать — раздутый и сложный в обслуживании код.

Вывод: UseCases — это инструменты, а не правила

Главный вывод: UseCases — это инструменты, а не правила. То, что Clean Architecture предполагает их, не означает, что их следует применять везде. Чрезмерное использование UseCases, особенно в ситуациях без сложной бизнес-логики, приводит к ненужной абстракции и раздутому коду.

Принимая решение о том, внедрять ли UseCases, спросите себя:

  • Есть ли бизнес-логика, которую нужно отделить от уровня представления?

  • Будет ли эта логика повторно использоваться в другом месте приложения?

  • Нужно ли проводить независимое тестирование этой логики?

Если ответ на эти вопросы — нет, пропустите UseCases и сделайте свой код простым. Если ответ — да, то UseCases поможет вам получить более чистую и более поддерживаемую кодовую базу.

Используйте UseCases с умом, и вы будете на правильном пути к написанию чистого и эффективного кода.

© Habrahabr.ru