Что такое чистая архитектура

Ответ

Чистая архитектура — это подход к проектированию приложений, где код разделяется на слои с четкими границами и зависимостями. Основная идея — бизнес-логика не зависит от фреймворков, UI или баз данных.

Основные слои:

  1. Domain — ядро с бизнес-правилами и use cases.
  2. Data — реализация репозиториев, работа с API/БД.
  3. Presentation — UI (Activity, Fragment, ViewModel).

Принципы:

  • Зависимости направлены внутрь (от внешних слоев к Domain).
  • Тестируемость и гибкость.

Пример (Kotlin):

// Domain
interface UserRepository {  
    suspend fun getUser(): User  
}  

// Data  
class UserRepositoryImpl : UserRepository {  
    override suspend fun getUser(): User = // fetch from API/DB  
}  

// Presentation  
class UserViewModel(repo: UserRepository) : ViewModel() {  
    val user = repo.getUser()  
}  

Ответ 18+ 🔞

Ну, слушай, вот есть у нас такая штука — чистая архитектура. Выглядит как очередная модная хуйня, которую придумали, чтобы мозги разработчикам выносить. Но на самом деле, если разобраться, идея-то пиздец какая здравая. Суть в том, чтобы твоё приложение не превращалось в одну большую, ебать мои старые костыли, спагетти-кашу, где всё связано со всем.

Представь: ты пишешь приложение, а потом заказчик говорит — «а давайте с Firebase на наш самописный бэкенд переедем», или «а давайте с XML на Compose перепишем». И ты такой сидишь и думаешь: «ёпта, ну это ж пиздец, полгода переписывать». Вот чтобы такого не было, и придумали эту самую чистоту.

Основная фишка — разделить всё на слои, как торт «Наполеон», только полезный. И главное правило — зависимости идут строго внутрь. То есть внешние слои (типа UI или базы данных) знают о внутренних (о бизнес-логике), а вот ядро приложения — нихуя не знает о том, как его рисуют на экране или куда там данные пишут. Овердохуища гибкости получается.

Слои, собственно, вот:

  1. Domain (Домен) — это святая святых, ядро. Тут живут твои бизнес-правила, твои модели данных и твои юзкейсы (то есть конкретные действия, которые может делать пользователь). Этот слой — как неприступная крепость. Он нихуя не знает про Android, про Retrofit или Room. Абсолютно чистый Kotlin. Идея в том, чтобы его можно было взять и перенести, условно, на сервер или на десктоп — и он бы работал.
  2. Data (Данные) — это уже реализация. Тут живут все эти репозитории, которые ты объявил в домене. Нужно получить юзера из сети? Пожалуйста — UserRepositoryImpl лезет в Retrofit. Нужно сохранить настройки? Шуршит в SharedPreferences. Этот слой знает и про домен, и про все внешние библиотеки. Он — слуга ядра.
  3. Presentation (Представление) — это всё, что видит пользователь: Activity, Fragment, ViewModel. Его задача — взять данные из домена (через репозитории) и красиво отрисовать. ViewModel, например, получает репозиторий из домена в конструктор и даже не парится, откуда там данные берутся — с сервера или из кэша.

Главные принципы, если коротко:

  • Зависимости внутрь: Внешнее зависит от внутреннего, а не наоборот. UI зависит от бизнес-логики, а логика не зависит от UI. Это, блядь, основа основ.
  • Тестируемость: Поскольку ядро ни от чего не зависит, его можно покрыть unit-тестами без всяких моков андроида — быстро и просто. Доверия ебать ноль ко всему, что не покрыто тестами.
  • Гибкость: Захотел сменить базу данных? Меняй слой Data, Domain даже не чихнет. Захотел новый UI? Да хуй с ним, пиши новый Presentation слой.

Ну и примерчик, чтобы стало совсем понятно:

// Domain (Ядро. Чистый Kotlin, нихуя не знает про Android)
// Объявляем контракт: "Ребята, кто бы вы ни были, вы должны уметь давать мне пользователя"
interface UserRepository {
    suspend fun getUser(): User
}

// Data (Слой реализации. Тут уже можем использовать что угодно)
class UserRepositoryImpl : UserRepository {
    // А вот тут уже можно и Retrofit, и Room, и что душе угодно
    override suspend fun getUser(): User = // fetch from API/DB
}

// Presentation (UI-слой. Android-specific штуки)
class UserViewModel(private val repo: UserRepository) : ViewModel() {
    // ViewModel получает репозиторий из домена. Ему похуй, как он реализован.
    val user: LiveData<User> = liveData {
        emit(repo.getUser())
    }
}

Вот и вся магия. Сначала кажется, что это овер-инженеринг и лишняя писанина. Но когда проект вырастает больше, чем «Hello World», и нужно что-то менять, ты понимаешь, что эта «лишняя писанина» спасает тебе кучу нервов и времени. Просто терпения ноль ебать на рефакторинг монолита.

Видео-ответы