Skip to main content
Kafka is built with a modern, scalable architecture that follows Android best practices and implements MVVM pattern with interactors. The architecture is inspired by Tivi and leverages the latest Android frameworks and Kotlin Multiplatform.

Architecture Principles

Kafka follows these core architectural principles:
  • Separation of Concerns: Clear separation between UI, domain, and data layers
  • Unidirectional Data Flow: Data flows from repositories through interactors to ViewModels
  • Reactive Programming: Heavy use of Kotlin Flows for reactive data streams
  • Dependency Injection: kotlin-inject for compile-time safe dependency injection
  • Modular Design: Feature-based modularization with shared core modules

Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                        UI Layer                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │   Compose    │  │  ViewModels  │  │  Navigation  │      │
│  │   Screens    │◄─┤              │  │              │      │
│  └──────────────┘  └──────┬───────┘  └──────────────┘      │
└────────────────────────────┼──────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                      Domain Layer                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ Interactors  │  │  Observers   │  │   Entities   │      │
│  │  (UseCases)  │  │ (SubjectInt) │  │              │      │
│  └──────┬───────┘  └──────┬───────┘  └──────────────┘      │
└─────────┼──────────────────┼──────────────────────────────────┘
          │                  │
          ▼                  ▼
┌─────────────────────────────────────────────────────────────┐
│                       Data Layer                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ Repositories │  │  Room DAOs   │  │  Remote API  │      │
│  │              │  │              │  │   (Ktor)     │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────────────────────────────────────────┘

Layer Responsibilities

UI Layer

The presentation layer built with Jetpack Compose:
  • Composable Screens: Declarative UI components
  • ViewModels: State management and user action handling
  • Navigation: Screen navigation and deep linking
  • State: Immutable state classes using StateFlow

Domain Layer

Business logic layer that orchestrates data flow:
  • Interactors: Execute single use cases (write operations)
  • Observers: Provide reactive streams of data (read operations)
  • Domain Models: Business entities and models
  • Mappers: Transform data between layers

Data Layer

Data management and persistence:
  • Repositories: Single source of truth for data
  • DAOs: Room database access objects
  • Remote Data Sources: API clients (Ktor)
  • Local Storage: Room database, DataStore preferences
  • Mappers: Convert between network/database and domain models

Technology Stack

UI Framework

  • Jetpack Compose
  • Material3 with MaterialYou
  • Compose Navigation

Architecture

  • MVVM Pattern
  • Repository Pattern
  • Interactors (Use Cases)

Async & Reactive

  • Kotlin Coroutines
  • Kotlin Flow
  • StateFlow & SharedFlow

Data Persistence

  • Room Database
  • DataStore (Preferences)
  • Firestore (Remote)

Networking

  • Ktor Client
  • Kotlin Serialization
  • OkHttp

Dependency Injection

  • kotlin-inject
  • @Component annotations
  • Compile-time safe

Media Playback

  • ExoPlayer
  • Sarahang (Audio)
  • Custom PDF/EPUB readers

Platform

  • Kotlin Multiplatform
  • Android & Desktop
  • Shared business logic

Key Architectural Patterns

Reactive Data Flow

Data flows reactively through the application:
  1. Repository observes database or remote data source
  2. Observer (SubjectInteractor) exposes a Flow
  3. ViewModel collects the Flow and transforms to UI state
  4. Composable collects state and renders UI
@ApplicationScope
class HomepageRepository @Inject constructor(
    private val firestoreGraph: FirestoreGraph,
    private val homepageMapper: HomepageMapper
) {
    fun observeHomepageCollection() =
        firestoreGraph.homepageCollection.snapshots
            .flatMapLatest { it.toHomepage() }
}

Separation of Read and Write Operations

  • Observers (SubjectInteractor): Read operations that return Flow<T>
  • Interactors: Write operations that return Flow<InvokeStatus>
  • ResultInteractors: Operations that return Result<T>
This separation provides clear intent and better type safety.

Kotlin Multiplatform

Many modules are marked with // kmp in settings.gradle, indicating they are shared between Android and Desktop:
  • base:domain - Core domain patterns
  • data:* - All data modules
  • domain - Business logic
  • ui:common, ui:components, ui:theme - Shared UI components

Next Steps

Module Structure

Explore the modular organization of the codebase

MVVM Pattern

Deep dive into ViewModels and Interactors

Data Layer

Learn about repositories, DAOs, and data management

UI Layer

Discover Jetpack Compose implementation

Build docs developers (and LLMs) love