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

Какие знаешь сущности в Presentation слое Clean Architecture?

2.0 Middle🔥 182 комментариев
#Архитектура и паттерны

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

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

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

Сущности Presentation слоя в Clean Architecture

В Clean Architecture (Чистой Архитектуре) Presentation слой отвечает за отображение данных пользователю и обработку пользовательского ввода. Этот слой содержит несколько ключевых сущностей, которые обеспечивают разделение ответственности и тестируемость. Основные сущности:

1. View (Activity/Fragment/Composable)

View отвечает за отображение UI и передачу пользовательских событий в Presenter/ViewModel. В современных подходах View должна быть максимально "глупой" и содержать только логику, связанную с отрисовкой.

class ProductListFragment : Fragment() {
    private val viewModel: ProductViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        viewModel.state.observe(viewLifecycleOwner) { state ->
            render(state) // Только отображение
        }
        
        binding.refreshButton.setOnClickListener {
            viewModel.onRefreshClicked() // Передача событий
        }
    }
    
    private fun render(state: ProductState) {
        when (state) {
            is ProductState.Loading -> showLoading()
            is ProductState.Success -> showProducts(state.products)
            is ProductState.Error -> showError(state.message)
        }
    }
}

2. Presenter/ViewModel

Presenter (в MVP) или ViewModel (в MVVM с Android Architecture Components) - это сущность, которая управляет состоянием View и обрабатывает бизнес-логику. Она получает данные из Domain слоя и преобразует их в формат, пригодный для отображения.

class ProductViewModel(
    private val getProductsUseCase: GetProductsUseCase,
    private val mapper: ProductMapper
) : ViewModel() {
    
    private val _state = MutableStateFlow<ProductState>(ProductState.Loading)
    val state: StateFlow<ProductState> = _state.asStateFlow()
    
    init {
        loadProducts()
    }
    
    fun onRefreshClicked() {
        loadProducts()
    }
    
    private fun loadProducts() {
        viewModelScope.launch {
            _state.value = ProductState.Loading
            try {
                val products = getProductsUseCase.execute()
                val uiModels = mapper.toUiModel(products)
                _state.value = ProductState.Success(uiModels)
            } catch (e: Exception) {
                _state.value = ProductState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

3. UI Models

UI Models (или View Models, Presentation Models) - это специализированные классы данных, оптимизированные для отображения в UI. Они отличаются от Domain Models и содержат только те поля, которые необходимы для отрисовки.

data class ProductUiModel(
    val id: String,
    val displayName: String,
    val formattedPrice: String,
    val isInStock: Boolean,
    val imageUrl: String,
    val rating: Float,
    val showDiscountBadge: Boolean,
    val discountText: String? = null
)

// Маппер для преобразования Domain Model в UI Model
class ProductMapper @Inject constructor(
    private val currencyFormatter: CurrencyFormatter,
    private val resourceProvider: ResourceProvider
) {
    fun toUiModel(domainProduct: Product): ProductUiModel {
        return ProductUiModel(
            id = domainProduct.id,
            displayName = domainProduct.name.toDisplayName(),
            formattedPrice = currencyFormatter.format(domainProduct.price),
            isInStock = domainProduct.quantity > 0,
            imageUrl = domainProduct.images.firstOrNull() ?: "",
            rating = domainProduct.rating,
            showDiscountBadge = domainProduct.discount > 0,
            discountText = if (domainProduct.discount > 0) {
                "-${domainProduct.discount}%"
            } else null
        )
    }
}

4. State Management

State Management сущности представляют различные состояния UI. Это обычно sealed классы или интерфейсы, которые инкапсулируют все возможные состояния экрана.

sealed class ProductState {
    object Loading : ProductState()
    data class Success(val products: List<ProductUiModel>) : ProductState()
    data class Error(val message: String) : ProductState()
    object Empty : ProductState()
}

5. Contract/Interface

Contract определяет взаимодействие между View и Presenter/ViewModel. Это особенно важно в подходе MVP для обеспечения тестируемости.

// Контракт для MVP подхода
interface ProductContract {
    interface View {
        fun showLoading()
        fun hideLoading()
        fun showProducts(products: List<ProductUiModel>)
        fun showError(message: String)
        fun showEmptyState()
    }
    
    interface Presenter {
        fun attachView(view: View)
        fun detachView()
        fun loadProducts()
        fun onProductClicked(productId: String)
        fun onRefresh()
    }
}

6. UI Components и Custom Views

Специализированные UI Components и Custom Views, которые инкапсулируют сложное поведение отображения.

7. Navigation Components

Navigation Components отвечают за переходы между экранами. В современных приложениях это может быть Jetpack Navigation Component или кастомная реализация навигации.

8. Adapters и ViewHolders

Для RecyclerView и других адаптеров списков используются Adapters и ViewHolders, которые также являются частью Presentation слоя.

Ключевые принципы Presentation слоя:

  • Пассивность View: View только отображает данные и передает события
  • Тестируемость: Бизнес-логика в Presenter/ViewModel легко тестируется без Android зависимостей
  • Однонаправленный поток данных: Данные текут в одном направлении (Domain → Presentation → View)
  • Отсутствие Domain логики: Presentation слой не должен содержать бизнес-правил
  • Реактивность: Использование LiveData, StateFlow, RxJava для реактивного обновления UI

Этот подход обеспечивает разделение ответственности, легкое тестирование и поддержку кода, позволяя заменять компоненты (например, менять фреймворки UI) без влияния на бизнес-логику приложения.