> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-agent-native-development.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture & Data Flow

> How chatuikit-core, chatuikit-compose, and chatuikit-kotlin modules are structured using Clean Architecture.

The UI Kit is split into three modules that follow Clean Architecture principles. `chatuikit-core` holds all business logic, data access, and state management. The UI modules (`chatuikit-compose` and `chatuikit-kotlin`) provide platform-specific rendering on top of the shared core.

## Module Structure

```mermaid theme={null}
graph TD
    subgraph UI["UI Modules"]
        A["<b>chatuikit-compose</b><br/><i>Jetpack Compose UI</i><br/>Composables, Themes"]
        B["<b>chatuikit-kotlin</b><br/><i>XML Views / ViewBinding</i><br/>Custom Views, Adapters"]
    end

    subgraph Core["chatuikit-core"]
        D["domain/ — usecase, repository interfaces, models"]
        E["data/ — datasource, repository implementations"]
        F["viewmodel/ — shared ViewModels"]
        G["state/ — sealed UIState classes"]
        H["events/ — CometChatEvents SharedFlow bus"]
        I["factory/ — ViewModel factories"]
        J["formatter, mentions, resources, utils"]
    end

    K["<b>CometChat SDK</b><br/>Chat SDK 4.x · Calls SDK 5.x"]

    A -->|depends on| Core
    B -->|depends on| Core
    Core -->|calls| K

```

## 4-Layer Architecture

Every feature follows the same layered flow: **View → ViewModel → Repository → DataSource**.

```mermaid theme={null}
block-beta
    columns 1
    A["View / Composable\nchatuikit-kotlin or chatuikit-compose"]
    B["ViewModel\nchatuikit-core"]
    C["Repository\nchatuikit-core"]
    D["DataSource\nchatuikit-core → CometChat SDK"]

    A --> B
    B --> C
    C --> D
```

The ViewModel lives in `chatuikit-core` and is shared by both UI modules. This means the same `CometChatConversationsViewModel` drives both the XML View and the Composable — only the rendering layer differs.

## Clean Architecture Layers (chatuikit-core)

### Data Layer

The data layer wraps the CometChat SDK behind interfaces, making it swappable and testable.

**DataSource** — defines the contract for raw SDK operations:

```kotlin theme={null}
// Interface (contract)
interface ConversationsDataSource {
    suspend fun fetchConversations(request: ConversationsRequest): List<Conversation>
    suspend fun deleteConversation(conversationWith: String, conversationType: String): String
    suspend fun markAsDelivered(message: BaseMessage)
}

// Implementation (calls CometChat SDK)
class ConversationsDataSourceImpl : ConversationsDataSource {
    override suspend fun fetchConversations(request: ConversationsRequest): List<Conversation> {
        // Wraps CometChat.fetchConversations() in a coroutine
    }
}
```

Every feature has a DataSource pair: `ConversationsDataSource` / `ConversationsDataSourceImpl`, `UsersDataSource` / `UsersDataSourceImpl`, etc.

**Repository Implementation** — coordinates data sources and handles error wrapping:

```kotlin theme={null}
class ConversationsRepositoryImpl(
    private val dataSource: ConversationsDataSource
) : ConversationsRepository {

    private var hasMore = true

    override suspend fun getConversations(
        request: ConversationsRequest
    ): Result<List<Conversation>> {
        return try {
            val conversations = dataSource.fetchConversations(request)
            hasMore = conversations.isNotEmpty()
            Result.success(conversations)
        } catch (e: CometChatException) {
            Result.failure(e)
        }
    }
}
```

Repositories wrap raw SDK exceptions into Kotlin `Result` types, track pagination state, and coordinate between data sources.

### Domain Layer

The domain layer defines contracts and single-purpose use cases. It has no dependency on the SDK or Android framework.

**Repository Interfaces** — contracts that the data layer implements:

```kotlin theme={null}
interface ConversationsRepository {
    suspend fun getConversations(request: ConversationsRequest): Result<List<Conversation>>
    suspend fun deleteConversation(conversationWith: String, conversationType: String): Result<Unit>
    suspend fun markAsDelivered(conversation: Conversation): Result<Unit>
    fun hasMoreConversations(): Boolean
}
```

**Use Cases** — encapsulate a single business action:

```kotlin theme={null}
open class GetConversationsUseCase(
    private val repository: ConversationsRepository
) {
    open suspend operator fun invoke(
        request: ConversationsRequest
    ): Result<List<Conversation>> {
        return repository.getConversations(request)
    }

    open fun hasMore(): Boolean = repository.hasMoreConversations()
}
```

Use cases are `open` so they can be overridden for testing or custom behavior. Each use case does one thing:

| Use Case | Action |
| - | - |
| `GetConversationsUseCase` | Fetch paginated conversations |
| `DeleteConversationUseCase` | Delete a conversation |
| `RefreshConversationsUseCase` | Clear and re-fetch |
| `FetchUsersUseCase` / `SearchUsersUseCase` | Fetch or search users |
| `FetchGroupsUseCase` | Fetch groups |
| `FetchGroupMembersUseCase` / `SearchGroupMembersUseCase` | Fetch or search group members |
| `BanGroupMemberUseCase` / `KickGroupMemberUseCase` | Member moderation |
| `ChangeMemberScopeUseCase` | Change member role |
| `SendTextMessageUseCase` / `SendMediaMessageUseCase` / `SendCustomMessageUseCase` | Send messages |
| `EditMessageUseCase` | Edit a sent message |
| `FetchCallLogsUseCase` | Fetch call history |
| `InitiateCallUseCase` / `InitiateUserCallUseCase` / `StartGroupCallUseCase` | Start calls |
| `FetchReactionsUseCase` / `RemoveReactionUseCase` | Reaction management |
| `CreatePollUseCase` | Create a poll |
| `GetStickersUseCase` | Fetch sticker sets |
| `JoinGroupUseCase` / `GetGroupUseCase` / `GetUserUseCase` | Entity lookups |

### ViewModel Layer

ViewModels receive use cases via constructor injection and expose `StateFlow` / sealed `UIState` classes to the UI:

```kotlin theme={null}
class CometChatConversationsViewModel(
    private val getConversationsUseCase: GetConversationsUseCase,
    private val deleteConversationUseCase: DeleteConversationUseCase,
    private val refreshConversationsUseCase: RefreshConversationsUseCase,
    private val enableListeners: Boolean = true
) : ViewModel() {

    // UI observes this sealed state
    private val _uiState = MutableStateFlow<UIState>(UIState.Loading)
    val uiState: StateFlow<UIState> = _uiState

    fun fetchConversations() {
        viewModelScope.launch {
            getConversationsUseCase(request)
                .onSuccess { conversations ->
                    _uiState.value = if (conversations.isEmpty()) UIState.Empty
                                     else UIState.Content(conversations)
                }
                .onFailure { _uiState.value = UIState.Error(it) }
        }
    }
}
```

### State Management (StateFlow + Sealed Classes)

Each feature has a dedicated sealed UIState class. All state is exposed via `StateFlow` — not LiveData:

```kotlin theme={null}
sealed class UIState {
    object Loading : UIState()
    object Empty : UIState()
    data class Error(val exception: CometChatException) : UIState()
    data class Content(val conversations: List<Conversation>) : UIState()
}
```

Feature-specific states include additional fields:

| State Class | Feature | Extra Fields |
| - | - | - |
| `ConversationStarterUIState` | Conversation list | — |
| `MessageListUIState` | Message list | scroll position, reply state |
| `MessageComposerUIState` | Composer | attachment state, edit mode |
| `MessageHeaderUIState` | Header | typing indicator, user status |
| `GroupsUIState` | Groups | — |
| `UsersUIState` | Users | — |
| `GroupMembersUIState` | Group members | scope change state |
| `CallLogsUIState` | Call logs | — |
| `CallButtonsUIState` | Call buttons | call initiation state |
| `IncomingCallUIState` / `OutgoingCallUIState` / `OngoingCallUIState` | Calls | call session state |
| `ReactionListUIState` | Reactions | — |
| `CreatePollUIState` | Polls | form validation state |
| `StickerKeyboardUIState` | Stickers | sticker sets |

### ListOperations Interface

List-based ViewModels implement the `ListOperations` interface, which provides a standard contract for manipulating list data:

```kotlin theme={null}
interface ListOperations<T> {
    fun addAtIndex(index: Int, item: T)
    fun updateAtIndex(index: Int, item: T)
    fun removeAtIndex(index: Int)
    fun moveToTop(item: T)
}
```

This ensures consistent list manipulation across Conversations, Users, Groups, Group Members, and Call Logs.

## Dependency Injection via Factories

ViewModels are created through `ViewModelProvider.Factory` classes that wire up the dependency chain:

```kotlin theme={null}
class CometChatConversationsViewModelFactory(
    private val repository: ConversationsRepository = ConversationsRepositoryImpl(
        ConversationsDataSourceImpl()
    ),
    private val enableListeners: Boolean = true
) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val getUseCase = GetConversationsUseCase(repository)
        val deleteUseCase = DeleteConversationUseCase(repository)
        val refreshUseCase = RefreshConversationsUseCase(repository)

        return CometChatConversationsViewModel(
            getConversationsUseCase = getUseCase,
            deleteConversationUseCase = deleteUseCase,
            refreshConversationsUseCase = refreshUseCase,
            enableListeners = enableListeners
        ) as T
    }
}
```

Default implementations are provided, but you can inject custom repositories:

```kotlin theme={null}
// Custom repository for testing or caching
val factory = CometChatConversationsViewModelFactory(
    repository = MyCustomConversationRepository(),
    enableListeners = false  // disable for previews
)
```

## Data Flow: End to End

### Fetching Conversations

```mermaid theme={null}
graph TD
    A["UI — Composable / View<br/><i>collects uiState</i>"] --> B["CometChatConversationsViewModel<br/><i>calls getConversationsUseCase</i>"]
    B --> C["GetConversationsUseCase<br/><i>calls repository.getConversations</i>"]
    C --> D["ConversationsRepositoryImpl<br/><i>calls dataSource, wraps Result, tracks pagination</i>"]
    D --> E["ConversationsDataSourceImpl<br/><i>calls CometChat SDK</i>"]
    E --> F["CometChat SDK → REST API → Response"]

```

### Real-Time Updates

```mermaid theme={null}
graph TD
    A["CometChat SDK — WebSocket<br/><i>onTextMessageReceived / onTypingStarted</i>"] --> B["ViewModel — SDK listener<br/><i>processes event, updates internal list</i>"]
    B --> C["Emits new UIState.Content — updatedList"]
    C --> D["UI recomposes / rebinds automatically"]

```

### User Action (Delete Conversation)

```mermaid theme={null}
graph TD
    A["UI — user swipes to delete"] --> B["ViewModel.deleteConversation"]
    B --> C["DeleteConversationUseCase"]
    C --> D["ConversationsRepositoryImpl"]
    D --> E["DataSource → CometChat SDK → REST API → 200 OK"]
    E --> F["ViewModel removes item → emits updated UIState"]

```

## Component ↔ Core Mapping

| UI Component | Core ViewModel | Use Cases | Repository | DataSource |
| - | - | - | - | - |
| Conversation List | `CometChatConversationsViewModel` | Get, Delete, Refresh | `ConversationsRepository` | `ConversationsDataSource` |
| Message List | `CometChatMessageListViewModel` | (message fetching) | `MessageListRepository` | `MessageListDataSource` |
| Message Composer | `CometChatMessageComposerViewModel` | SendText, SendMedia, SendCustom, Edit | `MessageComposerRepository` | `MessageComposerDataSource` |
| Message Header | `CometChatMessageHeaderViewModel` | — | `MessageHeaderRepository` | `MessageHeaderDataSource` |
| Users | `CometChatUsersViewModel` | FetchUsers, SearchUsers | `UsersRepository` | `UsersDataSource` |
| Groups | `CometChatGroupsViewModel` | FetchGroups, JoinGroup | `GroupsRepository` | `GroupsDataSource` |
| Group Members | `CometChatGroupMembersViewModel` | FetchMembers, Search, Ban, Kick, ChangeScope | `GroupMembersRepository` | `GroupMembersDataSource` |
| Call Logs | `CometChatCallLogsViewModel` | FetchCallLogs | `CallLogsRepository` | `CallLogsDataSource` |
| Call Buttons | `CometChatCallButtonsViewModel` | InitiateCall, StartGroupCall | `CallButtonsRepository` | `CallButtonsDataSource` |
| Reactions | `CometChatReactionListViewModel` | FetchReactions, RemoveReaction | `ReactionListRepository` | `ReactionListDataSource` |
| Message Info | `CometChatMessageInformationViewModel` | — | `MessageInformationRepository` | `MessageInformationDataSource` |

## Events (Cross-Component Communication)

The `CometChatEvents` singleton in `chatuikit-core` provides a typed event bus using Kotlin `SharedFlow` for communication between components that don't share a ViewModel:

| Event Flow | Emitted When |
| - | - |
| `CometChatEvents.messageEvents` | Message sent, edited, deleted, reacted |
| `CometChatEvents.conversationEvents` | Conversation deleted |
| `CometChatEvents.groupEvents` | Group created, member added/removed/banned |
| `CometChatEvents.userEvents` | User blocked/unblocked |
| `CometChatEvents.callEvents` | Call initiated, accepted, rejected |
| `CometChatEvents.uiEvents` | UI-level events (show dialog, navigate) |

See the [Events reference](/ui-kit/android/events) for sealed class types and subscription examples.

## Component-Level Overrides

Every layer in the architecture is designed to be replaceable. You can override at the level that makes sense for your use case — from swapping the entire data source to just tweaking a single use case.

### Override Points

```mermaid theme={null}
graph TD
    A["UI Component"] --> B["ViewModel ← inject via custom Factory"]
    B --> C["UseCase ← subclass (open classes)"]
    C --> D["Repository ← implement interface"]
    D --> E["DataSource ← implement interface"]
```

### 1. Custom Repository

The most common override. Implement the repository interface and pass it to the factory.

```kotlin theme={null}
class OfflineUsersRepository : UsersRepository {

    private val cachedUsers = listOf(/* local data */)

    override suspend fun getUsers(request: UsersRequest): Result<List<User>> {
        return Result.success(cachedUsers)
    }

    override fun hasMoreUsers(): Boolean = false
}
```

Wire it in:

<Tabs>
  <Tab title="Kotlin (XML Views)">
    ```kotlin theme={null}
    val factory = CometChatUsersViewModelFactory(
        repository = OfflineUsersRepository(),
        enableListeners = false
    )
    val viewModel = ViewModelProvider(this, factory)[CometChatUsersViewModel::class.java]

    val usersView = findViewById<CometChatUsers>(R.id.users)
    usersView.setViewModel(viewModel)
    ```
  </Tab>

  <Tab title="Jetpack Compose">
    ```kotlin theme={null}
    val factory = CometChatUsersViewModelFactory(
        repository = OfflineUsersRepository(),
        enableListeners = false
    )
    val viewModel: CometChatUsersViewModel = viewModel(factory = factory)

    CometChatUsers(
        usersViewModel = viewModel
    )
    ```
  </Tab>
</Tabs>

### 2. Custom DataSource

Override at the lowest level to change how SDK calls are made — useful for caching, offline support, or wrapping a different backend.

```kotlin theme={null}
class CachedUsersDataSource(
    private val sdkDataSource: UsersDataSourceImpl = UsersDataSourceImpl(),
    private val cache: UserCache
) : UsersDataSource {

    override suspend fun fetchUsers(request: UsersRequest): List<User> {
        val cached = cache.get(request)
        if (cached != null) return cached

        val users = sdkDataSource.fetchUsers(request)
        cache.put(request, users)
        return users
    }
}
```

Then wrap it in the default repository implementation:

```kotlin theme={null}
val dataSource = CachedUsersDataSource(cache = myCache)
val repository = UsersRepositoryImpl(dataSource)
val factory = CometChatUsersViewModelFactory(repository = repository)
```

### 3. Custom Use Cases

Use cases are `open` classes, so you can subclass them to add validation, logging, or transformation:

```kotlin theme={null}
class LoggingGetConversationsUseCase(
    repository: ConversationsRepository
) : GetConversationsUseCase(repository) {

    override suspend operator fun invoke(
        request: ConversationsRequest
    ): Result<List<Conversation>> {
        Log.d("Conversations", "Fetching conversations...")
        val result = super.invoke(request)
        result.onSuccess { Log.d("Conversations", "Fetched ${it.size} items") }
        result.onFailure { Log.e("Conversations", "Fetch failed", it) }
        return result
    }
}
```

### 4. Passing a Custom ViewModel to the UI Component

Both `chatuikit-compose` (Compose) and `chatuikit-kotlin` (XML Views) accept a pre-built ViewModel. This is the entry point for all overrides.

<Tabs>
  <Tab title="Kotlin (XML Views)">
    ```kotlin theme={null}
    class MyUsersActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_users)

            val factory = CometChatUsersViewModelFactory(
                repository = MyCustomUsersRepository()
            )
            val viewModel = ViewModelProvider(this, factory)[CometChatUsersViewModel::class.java]

            val usersView = findViewById<CometChatUsers>(R.id.users)
            usersView.setViewModel(viewModel)
        }
    }
    ```
  </Tab>

  <Tab title="Jetpack Compose">
    ```kotlin theme={null}
    @Composable
    fun MyUsersScreen() {
        val factory = CometChatUsersViewModelFactory(
            repository = MyCustomUsersRepository(),
            enableListeners = true
        )
        val viewModel: CometChatUsersViewModel = viewModel(factory = factory)

        CometChatUsers(
            usersViewModel = viewModel,
            style = CometChatUsersStyle.default(),
            hideToolbar = false,
            hideSearchBox = false
        )
    }
    ```
  </Tab>
</Tabs>

### 5. Disabling Listeners for Previews and Testing

Every factory accepts `enableListeners = false`. When disabled, the ViewModel won't register SDK listeners or UIKit event subscriptions — useful for Compose previews, unit tests, or showcase screens:

```kotlin theme={null}
val factory = CometChatConversationsViewModelFactory(
    repository = FakeConversationRepository(),
    enableListeners = false  // No WebSocket listeners, no event subscriptions
)
```

### Override Summary by Component

| Component | Factory Class | Repository Interface | Key Use Cases |
| - | - | - | - |
| Conversation List | `CometChatConversationsViewModelFactory` | `ConversationsRepository` | Get, Delete, Refresh |
| Users | `CometChatUsersViewModelFactory` | `UsersRepository` | FetchUsers, SearchUsers |
| Groups | `CometChatGroupsViewModelFactory` | `GroupsRepository` | FetchGroups, JoinGroup |
| Group Members | `CometChatGroupMembersViewModelFactory` | `GroupMembersRepository` | FetchMembers, Search, Ban, Kick, ChangeScope |
| Message List | `CometChatMessageListViewModelFactory` | `MessageListRepository` | (message fetching) |
| Message Composer | `CometChatMessageComposerViewModelFactory` | `MessageComposerRepository` | SendText, SendMedia, SendCustom, Edit |
| Call Logs | `CometChatCallLogsViewModelFactory` | `CallLogsRepository` | FetchCallLogs |
| Call Buttons | `CometChatCallButtonsViewModelFactory` | `CallButtonsRepository` | InitiateCall, StartGroupCall |
| Reactions | `CometChatReactionListViewModelFactory` | `ReactionListRepository` | FetchReactions, RemoveReaction |

## Related

* [Events](/ui-kit/android/events) — Cross-component communication via `CometChatEvents` SharedFlow
* [Methods](/ui-kit/android/methods) — UI Kit wrapper methods for init, auth, and messaging


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.