Clean Architecture — əsasları, Entities, Use Cases və Gateways təbəqələri

Müəllif: IT Sectr Dərc olunub: 2026-02-17 Oxuma vaxtı: 10 dəq

Clean Architecture — 2012-ci ildə Robert Martin (Uncle Bob) tərəfindən təklif edilmiş, tətbiqi müstəqil təbəqələrə ayıran çoxlaylı arxitektura: Domain (Entities, Use Cases), Data (Repositories, DataSources) və Presentation (ViewModels, Views). Əsas prinsip — Dependency Rule: asılılıqlar içəriyə yönəldilir, xarici təbəqələr daxili təbəqələrdən asılıdır, əksi yox. Clean Architecture yüksək mürəkkəblikli biznes məntiqi olan layihələr üçün mobil inkişafda tətbiq edilir. Ətraflı — The Clean Architecture kitabında.

Əsas məqamlar

  • Clean Architecture — üç təbəqə: Domain (biznes məntiqi), Data (məlumatlar), Presentation (UI) Dependency Rule ilə
  • Dependency Rule — asılılıqlar içəriyə yönəldilir, Domain Data və Presentation haqqında bilmir
  • Use Cases (Interactors) — biznes məntiqi ssenariləri, hər Use Case — bir metodlu bir sinif
  • Repository Interface — Domain-də məlumat abstraksiyası, Data təbəqəsində icra
  • Test edilə bilmə — Domain və Use Cases Android SDK və iOS UIKit olmadan unit testlərlə test edilir

Clean Architecture — çoxlaylı arxitekturanın əsasları

Clean Architecture — 2012-ci ildə Robert Martin (Uncle Bob) tərəfindən formalaşdırılmış arxitektura nümunəsi. Əsas fikir — tətbiqin sərt asılılıq qaydası ilə təbəqələrə bölünməsi: təbəqə daxilindəki kod xaricdəki kod haqqında bilmir. Xarici təbəqələr (UI, freymvorklar, verilənlər bazası) — icra detalları. Daxili təbəqələr (biznes məntiqi, müəssisə qaydaları) — tətbiqin mahiyyəti.

Clean Architecture təbəqələri mobil inkişafda: 1) Domain — Entities (biznes obyektləri) və Use Cases (istifadə ssenariləri); 2) Data — RepositoryImpl (repozitorilərin icrası), DataSources (şəbəkə, VB, keş); 3) Presentation — ViewModels, Views (Compose/SwiftUI). Domain — asılılıqları olmayan ən daxili təbəqə. Data Domain-dən asılıdır (repozitori interfeyslərini icra edir). Presentation Domain-dən asılıdır (Use Cases çağırır, nəticəyə abunə olur).

TəbəqəMəzmunAsılılıqlar
DomainEntities, Use Cases, Repository InterfacesYox (təmiz Kotlin/Swift)
DataRepositoryImpl, DataSources (API, DB, Cache)Domain, Retrofit, Room, Ktor
PresentationViewModels, Views, ComposablesDomain, Jetpack, SwiftUI

Dependency Rule — Clean Architecture-in yeganə sərt qaydası. Mənbə kodu yalnız öz daxilindəki təbəqəyə və ya aşağıdakı təbəqəyə (mərkəzə yaxın) istinad edə bilər. Presentation Domain-i import edir. Domain Data və ya Presentation-ı import etmir. Buna asılılıqların inversiyası (Dependency Inversion Principle) ilə nail olunur: Domain Repository interfeysini təyin edir, Data onu icra edir. Presentation konkret repozitoridən deyil, UseCase abstraksiyasından asılıdır.

Domain Layer: Entities, Use Cases və Repository Interfaces

Domain — tətbiqin ən sabit təbəqəsi. Entities — freymvorklardan asılı olmayan biznes obyektləri: User, Product, Order. Use Cases — bir invoke metodu (və ya Kotlin-də operator fun invoke) olan, bir ssenarini icra edən siniflər: GetUserUseCase, PlaceOrderUseCase, CalculateTotalUseCase. Repository Interfaces — Domain-də təyin edilən, Data-da icra edilən məlumat giriş abstraksiyaları. Domain Android SDK, iOS UIKit, Retrofit, Room ehtiva etmir — yalnız təmiz Kotlin və ya Swift.

swift
// Entity — biznes obyekti (Domain)
struct User: Equatable {
    let id: Int
    let name: String
    let email: String
}

// Repository Interface — məlumat abstraksiyası (Domain)
protocol UserRepository {
    func getUser(id: Int) async throws -> User
    func getUsers() async throws -> [User]
}

// Use Case — bir ssenari (Domain)
final class GetUserUseCase {
    private let repository: UserRepository

    init(repository: UserRepository) {
        self.repository = repository
    }

    func execute(id: Int) async throws -> User {
        return try await repository.getUser(id: id)
    }
}

Use Case — «bir metodlu sinif» — dogma deyil, praktiki tövsiyədir. Use Case mürəkkəbləşdikdə (validasiya + loqlama + repozitori çağırışı), onun metodları mənaya görə qruplaşdırılır: UserUseCase.getUser, UserUseCase.searchUsers, UserUseCase.deleteUser. Əsas odur ki, Use Case məlumatların haradan gəldiyini (şəbəkə, VB, keş) və kimin göstərdiyini (Compose, SwiftUI) bilməməlidir. IT Sectr-də biz hər bir biznes qaydası, yoxlama və ya iki mənbədən məlumat birləşdirmə əməliyyatı üçün Use Case ayırırıq.

Domain-in təmizliyi təbəqələrin sərhəddində DTO xəritələşdirilməsi ilə əldə edilir. Data təbəqəsi JSON modellərini (DTO) alır, onları Domain Entity-yə xəritələşdirir. Presentation Domain Entity-ni alır, ViewModel-ə (DisplayItem) xəritələşdirir. Domain Entity heç vaxt Retrofit, Room, Codable annotasiyalarını ehtiva etmir — bu, VB Room-dan Realm-ə və ya Retrofit-in Ktor-la əvəz edilməsi zamanı təbəqənin dəyişdirilməyəcəyinə zəmanət verir.

Data Layer: Repository Implementation və DataSources

Data Layer — Domain-də təyin edilmiş interfeyslərin icrası. RepositoryImpl (UserRepository-i icra edən siniflər) və DataSources (RemoteDataSource — API, LocalDataSource — VB, CacheDataSource — SharedPreferences/NSUserDefaults) ehtiva edir. Data təbəqəsi Domain-dən (repozitori interfeyslərini və Entities-i import edir) və freymvorklardan (Retrofit, Room, Ktor, CoreData) asılıdır. RepositoryImpl Domain-dən məlumat mənbəyini gizlədir — Use Case məlumatların şəbəkədən və ya keşdən gəldiyini bilmir.

kotlin
// DTO — şəbəkə üçün model (Data)
data class UserDto(
    @SerializedName("id") val id: Int,
    @SerializedName("first_name") val firstName: String,
    @SerializedName("last_name") val lastName: String,
    @SerializedName("email") val email: String
)

// RepositoryImpl — icra (Data)
class UserRepositoryImpl(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource
) : UserRepository {

    override suspend fun getUser(id: Int): User {
        // Keşdən almağa çalışırıq
        localDataSource.getUser(id)?.let { return it.toDomain() }
        // Yoxdursa — şəbəkədən yükləyirik
        val dto = remoteDataSource.fetchUser(id)
        val user = dto.toDomain()
        localDataSource.saveUser(user)
        return user
    }

    override suspend fun getUsers(): List<User> {
        return remoteDataSource.fetchAllUsers().map { it.toDomain() }
    }
}

// Mapper — DTO ↔ Domain çevrilməsi
fun UserDto.toDomain() = User(
    id = id,
    name = "$firstName $lastName",
    email = email
)

Keşləmə strategiyası Data Layer-də: RepositoryImpl əvvəlcə yerli yaddaşı yoxlayır, məlumat olmadıqda — şəbəkədən yükləyir və yerli olaraq saxlayır. Şəbəkə əlçatmazdırsa — isStale işarəsi ilə köhnəlmiş məlumatları qaytarır. Domain-də Use Case strategiya haqqında bilmir — User-i Repository.getUser(id) vasitəsilə alır. Strategiyanın dəyişdirilməsi (məsələn, hər 15 dəqiqədən bir keşin etibarsızlaşdırılması) Domain və Presentation-a təsir etmir.

Android-də modulluq — Kotlin Multiplatform Domain-i Android SDK-dan asılılıqları olmayan ayrıca KMP moduluna çıxarmağa imkan verir. Data — Domain-dən asılılığı olan ayrıca modul. Presentation — Domain-dən asılılığı olan Android modulu. Gradle asılılıqları: domain (pure Kotlin), data (domain + Retrofit + Room), app (domain + presentation + Hilt). Bu cür modulluq böyük layihələr üçün məcburidir — CI Domain-i ayrıca qurur, Domain-in unit testləri Android emulyatoru tələb etmir.

Presentation Layer: ViewModels və Views

Presentation Layer — Clean Architecture-in ən xarici təbəqəsi. ViewModels (Android) / ObservableObject (iOS) və Views (Compose/SwiftUI) ehtiva edir. ViewModel Use Case-i çağırır, nəticəni alır və UI vəziyyətinə (State) çevirir. View State-ə abunə olur və göstərir. Presentation Domain-dən asılıdır — Use Cases və Entities import edir. Presentation Data Layer-i import etmir — məlumatlar Use Case vasitəsilə gəlir, o da daxildə Repository istifadə edir.

Clean Architecture-də ViewModel biznes məntiqi ehtiva etmir — Use Case çağırır. Use Case User qaytarırsa, ViewModel onu UserDisplayItem-ə (name, emailFormatted, avatarUrl) — sırf prezentasiya modelinə çevirir. Use Case DisplayItem haqqında bilmir — Entity qaytarır. Bu bölgü Use Case-in UI olmadan və ViewModel-in UseCase olmadan (mock vasitəsilə) test edilməsinə imkan verir. IT Sectr-də biz ciddi şəkildə riayət edirik: Use Case — biznes məntiqi, ViewModel — yalnız prezentasiya, View — yalnız göstərmə.

kotlin
// Use Case (Domain) — təmiz biznes məntiqi
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: Int): User {
        return repository.getUser(id)
    }
}

// ViewModel (Presentation) — yalnız prezentasiya
class UserViewModel(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {

    private val _state = MutableStateFlow<UserScreenState>(UserScreenState.Loading)
    val state: StateFlow<UserScreenState> = _state.asStateFlow()

    fun loadUser(id: Int) {
        viewModelScope.launch {
            _state.value = UserScreenState.Loading
            val user = getUserUseCase(id)
            val displayItem = UserDisplayItem(
                name = user.name,
                email = user.email,
                initials = user.name.split(" ").joinToString("") { it.first().toString() }
            )
            _state.value = UserScreenState.Success(displayItem)
        }
    }
}

data class UserDisplayItem(
    val name: String,
    val email: String,
    val initials: String
)

sealed interface UserScreenState {
    data object Loading : UserScreenState
    data class Success(val displayItem: UserDisplayItem) : UserScreenState
    data class Error(val message: String) : UserScreenState
}

Navigation Presentation təbəqəsində — xarici halqanın hissəsidir. Clean Architecture naviqasiya mexanizmini təyin etmir — bu, NavController (Compose), NavigationStack (SwiftUI), Coordinator (UIKit) və ya Router (VIPER) ola bilər. Vacibdir: naviqasiya qərarını Presentation verir, lakin naviqasiya Use Case-ə nüfuz etməməlidir. Use Case nəticə qaytarır, ViewModel hansı ekrana keçəcəyinə qərar verir. Clean Architecture-də naviqasiya Domain dəyişdirilmədən əvəz edilə bilən bir detaldır.

Clean Architecture iOS və Android-də: kod nümunələri

Android-də Clean Architecture Gradle modulları vasitəsilə həyata keçirilir: domain (pure Kotlin), data (domain + Retrofit + Room), presentation (domain + Compose). Qovluq strukturu: domain/user/User.kt, GetUserUseCase.kt, UserRepository.kt; data/remote/UserRemoteDataSource.kt, local/UserDao.kt, repository/UserRepositoryImpl.kt; presentation/ui/user/UserViewModel.kt, UserScreen.kt. DI (Hilt) təbəqələri birləşdirir: UserRepositoryImpl domain modulunda UserRepository interfeysinə bağlanır.

iOS-də Clean Architecture ayrıca modullar olmadan SPM və ya Xcode qruplarından istifadə edir (Xcode məhdudiyyətlərinə görə). Domain — UIKit və ya SwiftUI import etməyən faylların olduğu qovluq. Data — APIClient, CoreDataStack, RepositoryImpl olan qovluq. Presentation — ViewModels və SwiftUI Views olan qovluq. DI konstruktor və ya App-də assembly vasitəsilə. Əsas çağırış — UI yeniləmələri üçün MainActor yoxlaması ilə UseCase.execute() vasitəsilə async/await.

swift
// Data Layer: Remote DataSource (iOS)
final class UserRemoteDataSource {
    private let apiClient: APIClient

    func fetchUser(id: Int) async throws -> UserDTO {
        return try await apiClient.get("/users/\(id)")
    }
}

// Repository Implementation (Data)
final class UserRepositoryImpl: UserRepository {
    private let remote: UserRemoteDataSource
    private let local: UserLocalDataSource

    func getUser(id: Int) async throws -> User {
        if let cached = try await local.getUser(id) {
            return cached
        }
        let dto = try await remote.fetchUser(id)
        let user = dto.toDomain()
        try await local.saveUser(user)
        return user
    }
}

// Presentation: ViewModel + SwiftUI View
@MainActor
final class UserViewModel: ObservableObject {
    @Published private(set) var state: UserScreenState = .loading
    private let getUserUseCase: GetUserUseCase

    func loadUser(id: Int) {
        Task {
            state = .loading
            if let user = try? await getUserUseCase.execute(id: id) {
                state = .success(user)
            } else {
                state = .error("Failed to load")
            }
        }
    }
}

IT Sectr layihələrində Clean Architecture — 30 gündən layihələr üçün standartımız. 2022-ci ildən Android/iOS üçün Kotlin Multiplatform ilə üçtəbəqəli arxitekturadan istifadə edirik. Domain — ümumi KMP modulu, Data — platforma modulları (Android-də Retrofit, iOS-də URLSession), Presentation — yerli UI. Bu, iOS və Android arasında biznes məntiqinin 60–80% ümumi kodunu verir, iki ayrıca icra ilə müqayisədə inkişaf vaxtını 30–40% qısaldır.

Tez-tez verilən suallar

Clean Architecture-də neçə təbəqə olmalıdır?

Minimal üç: Domain, Data, Presentation. Böyük layihələr üçün Framework (Android SDK/iOS UIKit asılılıqları) və Device (GPS, kamera, sensorlar) əlavə edirlər. Təbəqələrin sayı — sərt qayda deyil, rahatlıq məsələsidir. Əsas olan Dependency Rule-a riayət etməkdir: asılılıqlar içəriyə, Domain-ə yönəldilir. Üçdən başlayıb layihə böyüdükcə təbəqələr əlavə edə bilərsiniz.

Clean Architecture kodun miqdarını artırır?

Bəli — MVVM ilə müqayisədə 30–50% repozitori interfeysləri, Use Cases və mapper-lərin ayrılması hesabına. Sadə CRUD tətbiqi üçün bu həddindən artıqdır. Clean Architecture test edilə bilmə və təbəqələrin izolyasiyasının inkişaf sürətindən daha vacib olduğu mürəkkəb biznes məntiqi olan layihələr üçün əsaslandırılır. MVP və ya prototip üçün MVVM istifadə edin — Clean Architecture işə salmağı ləngidəcək.

Clean Architecture-i MVI ilə birləşdirmək olar?

Bəli, bu geniş yayılmış təcrübədir. Use Cases Domain-də qalır, Presentation isə MVI dövründən (Intent → Reducer → State) istifadə edir. Data Layer — eyni, Domain — eyni. Presentation-da MVI proqnozlaşdırıla bilən ekran vəziyyəti verir, Clean Architecture — biznes məntiqinin izolyasiyasını. Bu kombinasiya onlarla tərtibatçısı olan böyük layihələrdə istifadə olunur.

Hər məlumat sorğusu üçün Use Cases lazımdır?

Use Case əməliyyat biznes qaydası ehtiva etdikdə lazımdır: validasiya, iki mənbədən məlumat birləşdirmə, hesablama, loqlama, giriş hüquqlarının yoxlanması. Əlavə məntiq olmadan sadə getUser(id) sorğusu Repository-ni birbaşa ViewModel-dən çağıra bilər. Lakin arxitekturanın vahidliyi üçün bir çox komandalar Repository-nin hər ictimai metodu üçün Use Case yaradır — bu, 5–10% kod əlavə edir, lakin oxumanı asanlaşdırır.

Clean Architecture-i necə test etməli?

Domain: mock Repository ilə Use Cases-in unit testləri — Android SDK olmadan təmiz Kotlin/Swift. Data: mock/fake DataSource ilə RepositoryImpl-in inteqrasiya testləri. Presentation: mock UseCase ilə ViewModel testləri. Dependency Rule sayəsində hər təbəqə təcrid olunmuş şəkildə test edilir. IT Sectr-də Domain əhatəsi 95%-ə, Data — 70–80%, Presentation — 60–70% çatır.

Nəticələr

  • Clean Architecture — üç təbəqə (Domain, Data, Presentation) içəriyə doğru Dependency Rule ilə
  • Dependency Rule — Domain Data və Presentation haqqında bilmir, interfeyslər vasitəsilə izolyasiya
  • Domain — Entities, Use Cases, Repository Interfaces — freymvorksuz təmiz Kotlin/Swift
  • Data — RepositoryImpl, DataSources (şəbəkə, VB, keş) — Domain interfeyslərinin icrası
  • Presentation — ViewModels, Views — yalnız göstərmə, biznes məntiqi Use Cases-də
  • Test etmə — Domain 90–95% unit testlərlə əhatə olunur
  • KMP — Kotlin Multiplatform ilə Clean Architecture iOS + Android üçün 60–80% ümumi kod verir

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun