Adrian Kuta 2a419df43e feat(characters:presentation): UI-agnostic MVI ViewModel (REDI-87)
- CharacterListState (characters, isLoading, isLoadingNextPage, currentPage, endReached, error: UiText?),
  CharacterListAction (OnCharacterClick/OnRetry/OnLoadNextPage), CharacterListEvent (NavigateToDetail/ShowSnackbar).
- CharacterListViewModel: state via .update, one-time events via Channel, DataError -> UiText on failure,
  pagination persisted in SavedStateHandle (rebuilds list up to the saved page after process death).
- CharacterUi + Character.toCharacterUi().
- NO Compose/Views deps: verified no androidx.compose on the compile classpath. Stability via
  ImmutableList instead of @Stable (which would require compose-runtime) — the only compose-named
  transitive is kotlinx-immutable's annotations-only stub, not the Compose framework.
2026-06-10 12:43:30 +02:00

Android Architecture Showcase

A single runnable Android-only (Jetpack Compose) reference app that demonstrates good architecture conventions — each in its own module/example. Teaching repo: every module is meant to be minimal but complete and idiomatic.

Status: built milestone-by-milestone from the Linear backlog. Foundation (scaffold, version catalog, build-logic convention plugins) is complete and the project assembles green. Full architecture docs land with the Quality & Docs milestone.

Stack

Multi-module Gradle + build-logic convention plugins · Koin (constructor DSL) · Ktor · KotlinX Serialization · Coil · Kermit · type-safe Compose Navigation. Data comes from the no-key Rick & Morty API.

What it will showcase: MVI as the primary presentation pattern (flagship characters feature), an MVVM contrast screen, and the same MVI ViewModel driven by two renderers — Jetpack Compose and classic XML + ViewBinding + RecyclerView — proving the presentation logic is UI-toolkit-agnostic.

Module structure

:app                                  → wires everything; single Activity, Compose host
:build-logic                          → Gradle convention plugins (the only place versions/config live)
:core:domain                          → Result/error types, shared domain models  (pure Kotlin)
:core:data                            → Ktor HttpClient factory, safe-call helpers
:core:presentation                    → UiText, ObserveAsEvents, DataError → UiText
:core:design-system                   → AppTheme + reusable composables
:feature:characters:domain            → models + repository interface  (pure Kotlin)
:feature:characters:data              → DTOs, mappers, data source, repository impl
:feature:characters:presentation      → MVI ViewModel/State/Action/Event  (UI-agnostic: no Compose, no Views)
:feature:characters:presentation-compose → Compose renderer
:feature:characters:presentation-views   → Views/XML renderer (same ViewModel)
:feature:about:presentation           → MVVM contrast screen

Dependency rules: presentation → domain ← data; domain depends only on :core:domain; features never depend on other features; :app wires the graph.

Build & run

./gradlew assembleDebug     # build the APK
./gradlew projects          # print the module tree
./gradlew check             # tests + lint (added in the Quality & Docs milestone)

Requires JDK 17+ (the Gradle build pins a Java 17 toolchain) and the Android SDK (compileSdk 36, minSdk 24).

Description
No description provided
Readme 466 KiB
Languages
Kotlin 100%