← Назад к вопросам

Как реализовать связь бизнес логики и слой данных?

2.0 Middle🔥 181 комментариев
#Архитектура и паттерны#Работа с данными

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Реализация связи бизнес-логики и слоя данных в Android приложении

Связь между бизнес-логикой (доменным слоем) и слоем данных является фундаментальной для создания чистого, тестируемого и поддерживаемого приложения. В современной Android разработке для этого применяются несколько ключевых архитектурных паттернов и принципов.

Основные архитектурные подходы и принципы

  1. Принцип единственной ответственности (Separation of Concerns): Бизнес-логика должна быть независимой от конкретных деталей получения данных (сеть, база данных, файлы).
  2. Инверсия зависимостей (Dependency Inversion): Слой данных зависит от абстракций (интерфейсов), определенных бизнес-слоем, а не наоборот.
  3. Использование репозиториев (Repository Pattern): Это ключевой элемент, выступающий в роли посредника.

Реализация через паттерн Repository и Use Cases

Наиболее распространенный и рекомендуемый подход — комбинация Repository и Use Cases (или Interactors).

1. Слой данных: Repository

Репозиторий — это абстракция, которая определяет контракт для операций с данными (получение, сохранение). Он скрывает конкретные источники (LocalDataSource, RemoteDataSource).

// Интерфейс, объявленный в доменном слое. Слой данных его реализует.
interface UserRepository {
    suspend fun getUserById(id: String): Result<User>
    suspend fun updateUser(user: User): Result<Unit>
}

// Конкретная реализация в слое данных. Она объединяет разные источники.
class DefaultUserRepository(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource
) : UserRepository {

    override suspend fun getUserById(id: String): Result<User> {
        // Бизнес-правило: сначала проверяем локально, потом сеть.
        val localUser = localDataSource.getUser(id)
        if (localUser != null) {
            return Result.success(localUser)
        }
        return remoteDataSource.fetchUser(id)
    }
}

2. Доменный слой: Use Cases

Use Cases (или Interactors) содержат чистую бизнес-логику. Они зависят только от абстракций репозиториев и не знают, как данные получены.

// Use Case инкапсулирует конкретную бизнес-операцию.
class GetUserProfileUseCase(
    private val userRepository: UserRepository,
    private val analyticsTracker: AnalyticsTracker // Другие абстракции
) {
    suspend operator fun invoke(userId: String): Result<UserProfile> {
        // 1. Получаем данные через абстракцию репозитория.
        val userResult = userRepository.getUserById(userId)
        if (userResult.isFailure) {
            analyticsTracker.trackError("user_fetch_failed")
            return Result.failure(userResult.exceptionOrNull()!!)
        }

        val user = userResult.getOrThrow()

        // 2. Применяем бизнес-правила (преобразование, валидация).
        if (user.isActive.not()) {
            return Result.failure(InactiveUserException())
        }

        // 3. Возвращаем результат, специфичный для бизнес-логики.
        return Result.success(
            UserProfile(
                name = user.name,
                displayName = "${user.name} (${user.department})", // Форматирование
                canEdit = user.role == Role.ADMIN
            )
        )
    }
}

3. Связывание слоев: Dependency Injection

Для предоставления конкретных реализаций репозиториев Use Cases используется Dependency Injection (DI).

// Модуль в Dagger Hilt, Koin или другом DI-фреймворке.
@Module
@InstallIn(SingletonComponent::class)
object DataModule {

    @Provides
    @Singleton
    fun provideUserRepository(
        remoteDs: UserRemoteDataSource,
        localDs: UserLocalDataSource
    ): UserRepository {
        return DefaultUserRepository(remoteDs, localDs)
    }
}

@Module
@InstallIn(ActivityComponent::class)
object DomainModule {

    @Provides
    fun provideGetUserProfileUseCase(
        repo: UserRepository, // DI-фреймворк внедрит реализацию из DataModule
        tracker: AnalyticsTracker
    ): GetUserProfileUseCase {
        return GetUserProfileUseCase(repo, tracker)
    }
}

Ключевые преимущества такого подхода

  • Тестируемость: Бизнес-логику в Use Cases можно легко тестировать с помощью mock-репозиториев.
  • Замена источника данных: Можно изменить реализацию UserRepository (например, добавить новый API), не затрагивая десятки Use Cases.
  • Чистая архитектура: Слои имеют четкие границы и направление зависимостей (верхние слои зависят от абстракций нижних).
  • Многопоточность: Repositories и Use Cases, как правило, возвращают suspend функции или Flow, что позволяет естественно интегрировать корутины и управлять потоком данных из UI.

Таким образом, связь реализуется через абстракции (интерфейсы репозиториев), которые доменный слой определяет, а слой данных — реализует. Use Cases исполняют логику, используя эти абстракции, а Dependency Injection обеспечивает сборку всех компонентов. Это создает robust-архитектуру, готовую к масштабированию и изменениям.

Как реализовать связь бизнес логики и слой данных? | PrepBro