Skip to content

Android Architecture Essentials: Single Source of Truth (SSOT) and Unidirectional Data Flow (UDF) ​


🧭 1. Why Do We Need a Single Source of Truth (SSOT)? ​

In traditional Android development, we often write everything inside a single Activity or Fragment: UI logic, business logic, network calls, and state management.

At first, this might seem simple, but as your project grows, this approach leads to state inconsistency, poor maintainability, and even memory leaks.

Common Problems ​

  • Multiple copies of the same data across layers (UI, Repository, Network)
  • UI displaying outdated or inconsistent data
  • Tight coupling and hard-to-test code
  • Lifecycle issues causing state loss or memory leaks

The Solution ​

Define a Single Source of Truth (SSOT) β€” the only authoritative owner of data. All reads and writes go through this source. It represents the β€œone true” version of your data.


πŸ”‘ 2. What Is the Single Source of Truth? ​

Definition: SSOT means there is exactly one place where a particular piece of data is stored and modified. Every other part of the app observes this data but cannot directly alter it.

In Android architecture, the SSOT is typically implemented in a Repository or ViewModel:

  • Internally holds mutable data (MutableStateFlow, MutableLiveData)
  • Exposes immutable data (StateFlow, LiveData)
  • Provides methods or events to modify data safely

Benefits: ​

  • Centralized updates β†’ consistent data
  • Data protection from external mutation
  • Easier debugging and state tracking

πŸ”„ 3. Unidirectional Data Flow (UDF) ​

Unidirectional Data Flow (UDF) is a natural companion to SSOT. Its principle is simple:

Data flows downward; events flow upward.

Flow Diagram ​

User Action ─▢ ViewModel ─▢ Repository ─▢ Update Data Source
      β–²                                       β”‚
      └────────────── UI observes state β—€β”€β”€β”€β”€β”€β”€β”˜
  • Data flows down: from data layer to UI layer
  • Events flow up: from UI to data layer

Advantages: ​

  • Ensures consistent, predictable state
  • Easier debugging and logging
  • Clear separation of responsibilities

βš™οΈ 4. Practical Example: Implementing SSOT + UDF in MVVM ​

🧱 Repository (Single Source of Truth) ​

kotlin
class UserRepository {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user.asStateFlow()

    suspend fun fetchUser() {
        val result = api.getUser()
        _user.value = result
    }
}

🧩 ViewModel (Handles events + bridges data) ​

kotlin
class UserViewModel(private val repo: UserRepository): ViewModel() {
    val user = repo.user

    fun onRefresh() {
        viewModelScope.launch { repo.fetchUser() }
    }
}

🎨 UI Layer (Observes State) ​

kotlin
@Composable
fun UserScreen(vm: UserViewModel = viewModel()) {
    val user by vm.user.collectAsState()
    user?.let {
        Text("Hello, ${it.name}")
    } ?: CircularProgressIndicator()
}

βœ… Highlights

  • Only UserRepository owns the data
  • UI is read-only and reactive
  • State updates trigger automatic UI recomposition

🧠 5. SSOT Across Architectural Layers ​

LayerSSOT Example
Data LayerDatabase or Repository
ViewModel LayerUiState (StateFlow)
MVI ArchitectureState object as the single truth
Clean ArchitectureRepository per domain entity

🧩 6. Common Pitfalls & Optimization Tips ​

ProblemCauseSolution
Duplicate statesRepository + ViewModel each store dataMake Repository the sole owner
Mutable data exposedExposed MutableStateFlowExpose only read-only StateFlow
One-time events mixed with stateToasts or navigation inside state flowUse SharedFlow or Channel for events
Mixing LiveData & FlowInconsistent updatesUse Flow consistently across layers

πŸš€ 7. SSOT in Clean Architecture ​

In Clean Architecture, the layers flow as follows:

  • Data Layer: Repository + DataSource (DB, network)
  • Domain Layer: UseCase (business logic)
  • Presentation Layer: ViewModel + UI (state rendering)

Data moves upward through use cases, while immutable UI state flows downward. The Repository serves as the SSOT for each domain entity.

In modern Jetpack Compose + UDF setups, the UiState inside your ViewModel acts as the local SSOT, and your Composable UI is a pure function of that state.


🧩 8. Relationship to Redux / MVI ​

Redux and MVI architectures are built on the exact same principles β€” SSOT + UDF:

  • The Store is the only source of data truth
  • State is immutable
  • Actions are the only way to modify data

A modern Android ViewModel + StateFlow setup is essentially a lightweight Redux system β€” simpler, yet conceptually identical.


🧭 9. Summary ​

ConceptMeaningKey Benefits
SSOTSingle Source of TruthData consistency, centralized logic
UDFUnidirectional Data FlowPredictable and traceable state
Separation of ConcernsUI / Logic / Data decoupledHigh cohesion, low coupling
Immutable StateRead-only data exposurePrevents side effects & misuse

βœ… Key Takeaways ​

  • Keep one and only one source of truth
  • Maintain unidirectional, predictable data flow
  • UI should render state, not manage logic
  • SSOT + UDF are the cornerstones of modern Android architecture

Just something casual. Hope you like it. Built with VitePress