Communitygithub.com

android-clean-architecture (Android Clean Architecture)

Clean Architecture-Muster für Android- und Kotlin-Multiplatform-Projekte – Modulstruktur, Abhängigkeitsregeln, UseCases, Repositories und Datenebenenmuster. Aktivieren Sie dieses Skill, wenn Sie Module, Schichten oder Datenflüsse in einem Android- oder KMP-Projekt strukturieren.

Was ist android-clean-architecture (Android Clean Architecture)?

Dieses Skill dokumentiert die Clean Architecture-Prinzipien für Android- und Kotlin Multiplatform (KMP)-Projekte. Es definiert eine klare modulare Projektstruktur mit strikten Abhängigkeitsregeln, wobei die Domänenschicht („domain“) rein in Kotlin gehalten wird und niemals von frameworkspezifischen Schichten wie „data“ oder „presentation“ abhängt. Das Skill erklärt die Implementierung von UseCases, Repository-Interfaces und DataSources, die Datenmigration mittels Mapper-Pattern sowie die Einbindung von Room, SQLDelight und Ktor. Es unterstützt sowohl Koin (KMP-kompatibel) als auch Hilt für die Abhängigkeitsinjektion, zeigt die Fehlerbehandlung mit Result- oder Try-Typen und gibt Hinweise auf Anti-Patterns wie fette Repositories oder die Verwendung von Android-Klassen in der Domäne. Für Entwickler, die wartbare, testbare und skalierbare Android- und KMP-Anwendungen nach Clean Architecture erstellen möchten, bietet dieses Skill eine umfassende, praxisnahe Anleitung.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/android-clean-architecture

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Android Clean Architecture

Clean Architecture-Muster für Android- und KMP-Projekte. Behandelt Modulgrenzen, Abhängigkeitsinversion, UseCase-/Repository-Muster und Datenebenen-Design mit Room, SQLDelight und Ktor.

Wann aktivieren

  • Strukturierung von Android- oder KMP-Projektmodulen
  • Implementierung von UseCases, Repositories oder DataSources
  • Entwurf von Datenflüssen zwischen Schichten (domain, data, presentation)
  • Einrichtung von Abhängigkeitsinjektion mit Koin oder Hilt
  • Arbeiten mit Room, SQLDelight oder Ktor in einer geschichteten Architektur

Modulstruktur

Empfohlenes Layout

project/
├── app/                  # Android-Einstiegspunkt, DI-Verkabelung, Application-Klasse
├── core/                 # Gemeinsame Hilfsmittel, Basisklassen, Fehlertypen
├── domain/               # UseCases, Domänenmodelle, Repository-Schnittstellen (reines Kotlin)
├── data/                 # Repository-Implementierungen, DataSources, DB, Netzwerk
├── presentation/         # Bildschirme, ViewModels, UI-Modelle, Navigation
├── design-system/        # Wiederverwendbare Compose-Komponenten, Theme, Typografie
└── feature/              # Feature-Module (optional, für größere Projekte)
    ├── auth/
    ├── settings/
    └── profile/

Abhängigkeitsregeln

app → presentation, domain, data, core
presentation → domain, design-system, core
data → domain, core
domain → core (oder keine Abhängigkeiten)
core → (nichts)

Wichtig: domain darf NIEMALS von data, presentation oder einem Framework abhängen. Es enthält nur reines Kotlin.

Domänenschicht

UseCase-Muster

Jeder UseCase repräsentiert eine Geschäftsoperation. Verwenden Sie operator fun invoke für saubere Aufrufe:

class GetItemsByCategoryUseCase(
    private val repository: ItemRepository
) {
    suspend operator fun invoke(category: String): Result<List<Item>> {
        return repository.getItemsByCategory(category)
    }
}

// Flow-basierter UseCase für reaktive Streams
class ObserveUserProgressUseCase(
    private val repository: UserRepository
) {
    operator fun invoke(userId: String): Flow<UserProgress> {
        return repository.observeProgress(userId)
    }
}

Domänenmodelle

Domänenmodelle sind einfache Kotlin-Data-Klassen ohne Framework-Annotationen:

data class Item(
    val id: String,
    val title: String,
    val description: String,
    val tags: List<String>,
    val status: Status,
    val category: String
)

enum class Status { DRAFT, ACTIVE, ARCHIVED }

Repository-Schnittstellen

Definiert in der Domäne, implementiert in der Datenebene:

interface ItemRepository {
    suspend fun getItemsByCategory(category: String): Result<List<Item>>
    suspend fun saveItem(item: Item): Result<Unit>
    fun observeItems(): Flow<List<Item>>
}

Datenebene

Repository-Implementierung

Koordiniert zwischen lokalen und entfernten Datenquellen:

class ItemRepositoryImpl(
    private val localDataSource: ItemLocalDataSource,
    private val remoteDataSource: ItemRemoteDataSource
) : ItemRepository {

    override suspend fun getItemsByCategory(category: String): Result<List<Item>> {
        return runCatching {
            val remote = remoteDataSource.fetchItems(category)
            localDataSource.insertItems(remote.map { it.toEntity() })
            localDataSource.getItemsByCategory(category).map { it.toDomain() }
        }
    }

    override suspend fun saveItem(item: Item): Result<Unit> {
        return runCatching {
            localDataSource.insertItems(listOf(item.toEntity()))
        }
    }

    override fun observeItems(): Flow<List<Item>> {
        return localDataSource.observeAll().map { entities ->
            entities.map { it.toDomain() }
        }
    }
}

Mapper-Muster

Halten Sie Mapper als Erweiterungsfunktionen nahe den Datenmodellen:

// In der Datenebene
fun ItemEntity.toDomain() = Item(
    id = id,
    title = title,
    description = description,
    tags = tags.split("|"),
    status = Status.valueOf(status),
    category = category
)

fun ItemDto.toEntity() = ItemEntity(
    id = id,
    title = title,
    description = description,
    tags = tags.joinToString("|"),
    status = status,
    category = category
)

Room-Datenbank (Android)

@Entity(tableName = "items")
data class ItemEntity(
    @PrimaryKey val id: String,
    val title: String,
    val description: String,
    val tags: String,
    val status: String,
    val category: String
)

@Dao
interface ItemDao {
    @Query("SELECT * FROM items WHERE category = :category")
    suspend fun getByCategory(category: String): List<ItemEntity>

    @Upsert
    suspend fun upsert(items: List<ItemEntity>)

    @Query("SELECT * FROM items")
    fun observeAll(): Flow<List<ItemEntity>>
}

SQLDelight (KMP)

-- Item.sq
CREATE TABLE ItemEntity (
    id TEXT NOT NULL PRIMARY KEY,
    title TEXT NOT NULL,
    description TEXT NOT NULL,
    tags TEXT NOT NULL,
    status TEXT NOT NULL,
    category TEXT NOT NULL
);

getByCategory:
SELECT * FROM ItemEntity WHERE category = ?;

upsert:
INSERT OR REPLACE INTO ItemEntity (id, title, description, tags, status, category)
VALUES (?, ?, ?, ?, ?, ?);

observeAll:
SELECT * FROM ItemEntity;

Ktor-Netzwerkclient (KMP)

class ItemRemoteDataSource(private val client: HttpClient) {

    suspend fun fetchItems(category: String): List<ItemDto> {
        return client.get("api/items") {
            parameter("category", category)
        }.body()
    }
}

// HttpClient-Einrichtung mit Content Negotiation
val httpClient = HttpClient {
    install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
    install(Logging) { level = LogLevel.HEADERS }
    defaultRequest { url("https://api.example.com/") }
}

Abhängigkeitsinjektion

Koin (KMP-kompatibel)

// Domänenmodul
val domainModule = module {
    factory { GetItemsByCategoryUseCase(get()) }
    factory { ObserveUserProgressUseCase(get()) }
}

// Datenmodul
val dataModule = module {
    single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
    single { ItemLocalDataSource(get()) }
    single { ItemRemoteDataSource(get()) }
}

// Präsentationsmodul
val presentationModule = module {
    viewModelOf(::ItemListViewModel)
    viewModelOf(::DashboardViewModel)
}

Hilt (nur Android)

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    abstract fun bindItemRepository(impl: ItemRepositoryImpl): ItemRepository
}

@HiltViewModel
class ItemListViewModel @Inject constructor(
    private val getItems: GetItemsByCategoryUseCase
) : ViewModel()

Fehlerbehandlung

Result/Try-Muster

Verwenden Sie Result<T> oder einen benutzerdefinierten versiegelten Typ für die Fehlerweitergabe:

sealed interface Try<out T> {
    data class Success<T>(val value: T) : Try<T>
    data class Failure(val error: AppError) : Try<Nothing>
}

sealed interface AppError {
    data class Network(val message: String) : AppError
    data class Database(val message: String) : AppError
    data object Unauthorized : AppError
}

// In ViewModel – in den UI-Status abbilden
viewModelScope.launch {
    when (val result = getItems(category)) {
        is Try.Success -> _state.update { it.copy(items = result.value, isLoading = false) }
        is Try.Failure -> _state.update { it.copy(error = result.error.toMessage(), isLoading = false) }
    }
}

Konventions-Plugins (Gradle)

Verwenden Sie für KMP-Projekte Konventions-Plugins, um Build-Datei-Duplizierung zu reduzieren:

// build-logic/src/main/kotlin/kmp-library.gradle.kts
plugins {
    id("org.jetbrains.kotlin.multiplatform")
}

kotlin {
    androidTarget()
    iosX64(); iosArm64(); iosSimulatorArm64()
    sourceSets {
        commonMain.dependencies { /* gemeinsame Abhängigkeiten */ }
        commonTest.dependencies { implementation(kotlin("test")) }
    }
}

In Modulen anwenden:

// domain/build.gradle.kts
plugins { id("kmp-library") }

Anti-Patterns, die vermieden werden sollten

  • Importieren von Android-Framework-Klassen in domain – halten Sie es rein in Kotlin
  • Datenbankentitäten oder DTOs für die UI-Schicht freigeben – immer auf Domänenmodelle abbilden
  • Geschäftslogik in ViewModels unterbringen – in UseCases auslagern
  • Verwendung von GlobalScope oder unstrukturierten Coroutinen – viewModelScope oder strukturierte Nebenläufigkeit verwenden
  • Dicke Repository-Implementierungen – in fokussierte DataSources aufteilen
  • Zirkuläre Modulabhängigkeiten – wenn A von B abhängt, darf B nicht von A abhängen

Referenzen

Siehe Skill: compose-multiplatform-patterns für UI-Muster. Siehe Skill: kotlin-coroutines-flows für asynchrone Muster.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills