# How Clean Architecture Is Implemented in the Bitchat Android Codebase

> Discover how Bitchat Android implements Clean Architecture isolating business logic from framework code using four distinct layers: Presentation, Domain, Data, and Platform.

- Repository: [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android)
- Tags: architecture
- Published: 2026-07-28

---

**Bitchat Android implements Clean Architecture through four concentric layers—Presentation, Domain, Data, and Platform—enforcing strict inward-facing dependencies that keep Android framework code isolated from business logic.**

The permissionlesstech/bitchat-android repository demonstrates a mature Clean Architecture implementation that organizes code around feature-focused packages while maintaining loosely-coupled layer boundaries. This architectural approach separates concerns into distinct rings, making the core application logic highly testable and keeping platform-specific dependencies confined to the outermost edges of the system.

## Layered Package Structure

The Clean Architecture implementation in Bitchat Android organizes source files into four concentric layers, each with specific responsibilities and dependency constraints.

### Presentation Layer (UI)

The **Presentation** layer resides in `com.bitchat.android.ui` and contains Jetpack Compose screens plus ViewModels that expose state via `StateFlow` or `LiveData`. In [`app/src/main/java/com/bitchat/android/ui/MainViewModel.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/ui/MainViewModel.kt), the ViewModel serves as the primary state holder, consuming domain-level abstractions like `NostrRelayManager` while remaining completely agnostic to networking implementations or platform APIs.

### Domain Layer (Business Logic)

The **Domain** layer contains pure Kotlin classes that encapsulate core application logic without Android framework dependencies. Located in packages such as `com.bitchat.android.nostr` and `com.bitchat.android.identity`, this layer includes [`NostrRelayManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/NostrRelayManager.kt) and [`SecureIdentityStateManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/SecureIdentityStateManager.kt). These files orchestrate business rules—such as cryptographic identity management and Nostr protocol handling—independently of how data is persisted or transmitted.

### Data and Infrastructure Layer

The **Data** layer implements interfaces defined by the domain, handling interactions with external systems like Bluetooth Low Energy (BLE), Tor networking, and file I/O. Files in `com.bitchat.android.service` and `com.bitchat.android.mesh`—including [`MeshForegroundService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MeshForegroundService.kt) and [`NoiseEncryptionService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/NoiseEncryptionService.kt)—provide concrete implementations that satisfy domain contracts while managing Android-specific concerns like background execution permissions and hardware access.

### Platform Layer (External)

The outermost **Platform** layer contains low-level native code and JNI bridges in `info.guardianproject.arti`. The [`ArtiNative.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/ArtiNative.kt) file provides the bridge to the Arti Tor implementation, representing the infrastructure boundary that depends on Android NDK and native libraries. This layer is completely isolated from business logic; it is only invoked through abstractions managed by the data layer.

## Dependency Rules and Boundaries

The **dependency direction** in Bitchat Android always flows inward: UI → Domain → Data → Platform. UI components never invoke platform APIs directly; instead, they reference use-case classes from the domain layer, which in turn call repository interfaces implemented in the data layer. This strict boundary ensures that changes to networking implementations, cryptographic libraries, or native code remain confined to their respective rings without cascading into business logic or presentation code.

## Key Architectural Patterns

### ViewModel-Driven UI

Each screen maintains an associated ViewModel that holds UI state and interacts with domain-level interactors. The `MainViewModel` depends on `NostrRelayManager` and `MeshForegroundService` abstractions, consuming `StateFlow` emissions while remaining unaware of how those events are produced or transmitted across the mesh network.

### Repository Pattern

The domain layer defines abstraction interfaces for data operations, satisfied by concrete implementations in the data layer. Domain code depends on interfaces like `NostrRelayRepository`, while `NostrRelayRepositoryImpl` in the data layer handles actual OkHttp network requests. This inversion of dependencies ensures that business rules never directly import `okhttp3` or Android networking classes.

### Service Facades

Background services expose thin façades that preserve separation of concerns. `MeshForegroundService` provides a public API consumed via coroutine flows in the presentation layer, preventing direct coupling between UI components and low-level networking code that manages BLE scanning or Tor circuit establishment.

## Clean Architecture Code Examples

The following examples demonstrate how Clean Architecture separates concerns across the codebase:

### ViewModel Consuming Domain Use-Case

```kotlin
class MainViewModel(
    private val nostrRelayManager: NostrRelayManager,
    private val meshService: MeshForegroundService
) : ViewModel() {

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

    init {
        viewModelScope.launch {
            nostrRelayManager.events.collect { event ->
                _state.value = UiState.NewEvent(event)
            }
        }
    }
}

```

This `MainViewModel` lives in the **presentation** layer and depends only on domain-level abstractions.

### Domain-Level Use-Case Orchestrating Mesh Networking

```kotlin
class PeerDiscoveryUseCase(
    private val meshService: MeshForegroundService,
    private val identityManager: SecureIdentityStateManager
) {
    suspend fun startDiscovery() {
        val myId = identityManager.currentIdentity()
        meshService.startScanning(myId)
    }
}

```

Implemented in the **domain** layer, this use-case calls the concrete `MeshForegroundService` (data layer) only through its public API contract, maintaining the architectural boundary.

### Data-Layer Repository Implementation

```kotlin
class NostrRelayRepositoryImpl(
    private val httpClient: OkHttpClient
) : NostrRelayRepository {

    override suspend fun fetchRelays(): List<NostrRelay> {
        // Network request using OkHttp (platform code)
    }
}

```

This class resides in the **data** package and satisfies a domain interface, isolating OkHttp dependencies from business logic.

## Summary

- **Clean Architecture** in Bitchat Android strictly separates concerns into four layers: Presentation (`com.bitchat.android.ui`), Domain (`com.bitchat.android.nostr`, `com.bitchat.android.identity`), Data (`com.bitchat.android.service`, `com.bitchat.android.mesh`), and Platform (`info.guardianproject.arti`).
- **Dependencies always point inward**, ensuring business logic in `SecureIdentityStateManager` and `NostrRelayManager` remains independent of Android framework code and external libraries like OkHttp or Arti.
- **Testability** is achieved by isolating pure Kotlin domain classes that can be unit-tested without Android instrumentation or mocking complex system services.
- Key files like [`MainViewModel.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MainViewModel.kt), [`MeshForegroundService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MeshForegroundService.kt), and [`ArtiNative.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/ArtiNative.kt) demonstrate clear boundaries between UI state management, infrastructure concerns, and low-level native implementations.

## Frequently Asked Questions

### What are the four layers in Bitchat Android's Clean Architecture?

The architecture consists of the **Presentation** layer (UI components and ViewModels), **Domain** layer (pure Kotlin business logic), **Data** layer (infrastructure and external system implementations), and **Platform** layer (low-level native code and JNI bridges). This structure ensures that dependencies only flow inward toward the domain core, keeping business rules isolated from framework details.

### How does Bitchat Android maintain testability with Clean Architecture?

By isolating business logic in pure Kotlin classes within the domain layer, such as `SecureIdentityStateManager` and `PeerDiscoveryUseCase`, the codebase allows for unit testing without Android framework dependencies. The separation of interfaces and implementations—where domain code depends on abstractions like `NostrRelayRepository` rather than concrete `NostrRelayRepositoryImpl`—enables mocking of data layer dependencies during domain testing.

### Where is the dependency direction enforced in the codebase?

Dependency direction is enforced through package structure and import constraints: files in `com.bitchat.android.ui` import domain classes from `com.bitchat.android.nostr`, but never import platform-specific code from `info.guardianproject.arti` or Android service implementations directly. This creates a compile-time boundary that prevents layer violations, ensuring that `MainViewModel` cannot accidentally reference `ArtiNative` or OkHttp client implementations.

### How are background services integrated without breaking Clean Architecture?

Services like `MeshForegroundService` implement interfaces that the domain layer can reference, and they expose functionality through coroutine flows or façade patterns. This allows ViewModels in the presentation layer to consume mesh networking capabilities—such as peer discovery and message relay—while maintaining strict separation between UI code and platform-specific background service logic that manages BLE advertisements or Tor circuit management.