haidrrrry/compose-kotlin-agent-skills · Archived

android-kotlin-architecture

Enforces Clean Architecture, MVVM/MVI, and unidirectional data flow for Android apps. Covers UiState, UiEvent, UiEffect, UseCase boundaries, module structure, and atomic ViewModel state via _state.update. Use when structuring features, refactoring ViewModels, splitting modules, or asking "MVVM vs MVI", "where does state live", "UseCase when".

First seen Jun 17, 2026

Installation

$ npx skills add haidrrrry/compose-kotlin-agent-skills --skill android-kotlin-architecture

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from haidrrrry/compose-kotlin-agent-skills.

npx skills add haidrrrry/compose-kotlin-agent-skills

Browse all from haidrrrry/compose-kotlin-agent-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 44
License LICENSE
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,009 B
  • docs SUMMARY.md 379 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 3 installs

SKILL.md

Android Kotlin — Architecture Module

Parent kit: [../../SKILL.md](../../SKILL.md) · Index: [../../AGENTS.md](../../AGENTS.md)

Read First

File When
[../../references/01-architecture.md](../../references/01-architecture.md) Full MVVM/MVI patterns, container/content composables
[../../references/04-coroutines-flow.md](../../references/04-coroutines-flow.md) StateFlow, Channel effects, stateIn
[../../references/05-hilt-di.md](../../references/05-hilt-di.md) @HiltViewModel, repository wiring
[../../references/06-room-db.md](../../references/06-room-db.md) Repository + Room offline-first

MVI Contract (Mandatory)

@Immutable
data class FeatureUiState(
    val items: List<ItemUi> = emptyList(),
    val isLoading: Boolean = false,
    val error: UiError? = null
)

sealed interface FeatureUiEvent {
    data class SearchChanged(val query: String) : FeatureUiEvent
    data object Refresh : FeatureUiEvent
    data class ItemClicked(val id: String) : FeatureUiEvent
}

sealed interface FeatureUiEffect {
    data class ShowMessage(@StringRes val messageRes: Int) : FeatureUiEffect
    data class NavigateToDetail(val id: String) : FeatureUiEffect
}

class FeatureViewModel @Inject constructor(
    private val repository: FeatureRepository
) : ViewModel() {

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

    private val _effects = Channel<FeatureUiEffect>(Channel.BUFFERED)
    val effects: Flow<FeatureUiEffect> = _effects.receiveAsFlow()

    fun onEvent(event: FeatureUiEvent) {
        when (event) {
            is FeatureUiEvent.SearchChanged -> onSearchChanged(event.query)
            FeatureUiEvent.Refresh -> refresh()
            is FeatureUiEvent.ItemClicked -> emitNavigate(event.id)
        }
    }

    private fun onSearchChanged(query: String) {
        _state.update { it.copy(searchQuery = query) }  // ONLY mutation style allowed
    }

    private fun refresh() {
        viewModelScope.launch {
            _state.update { it.copy(isLoading = true, error = null) }
            repository.sync()
                .onSuccess { _state.update { it.copy(isLoading = false) } }
                .onFailure { e ->
                    _state.update { it.copy(isLoading = false, error = e.toUiError()) }
                }
        }
    }

    private fun emitNavigate(id: String) {
        viewModelScope.launch { _effects.send(FeatureUiEffect.NavigateToDetail(id)) }
    }
}

Module Layout (Large Apps)

app/
feature-<name>/
  presentation/   # Composables + ViewModel
  domain/         # Models, repository interfaces, UseCases
  data/           # Room, Ktor, mappers
core/
  core-ui/        # Design system
  core-domain/
  core-testing/

Feature modules must not depend on other feature modules — only core-* and domain contracts.

UseCase Rule

Use UseCase Skip UseCase
Logic shared by 2+ ViewModels Single-line repo delegate
Multi-repo orchestration One ViewModel, one repo
Needs isolated unit tests CRUD with no extra rules

Anti-Patterns (Architecture)

Banned Fix
_state.value = newState _state.update { it.copy(...) }
God ViewModel (>200 lines) Split screen or extract UseCases
ViewModel in composable params Pass onEvent lambdas
Domain model with @Entity Entity in data/, map in repository
Collecting Flow in init without stateIn stateIn(WhileSubscribed(5_000))