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) β
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) β
class UserViewModel(private val repo: UserRepository): ViewModel() {
val user = repo.user
fun onRefresh() {
viewModelScope.launch { repo.fetchUser() }
}
}
π¨ UI Layer (Observes State) β
@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 β
Layer | SSOT Example |
---|---|
Data Layer | Database or Repository |
ViewModel Layer | UiState (StateFlow) |
MVI Architecture | State object as the single truth |
Clean Architecture | Repository per domain entity |
π§© 6. Common Pitfalls & Optimization Tips β
Problem | Cause | Solution |
---|---|---|
Duplicate states | Repository + ViewModel each store data | Make Repository the sole owner |
Mutable data exposed | Exposed MutableStateFlow | Expose only read-only StateFlow |
One-time events mixed with state | Toasts or navigation inside state flow | Use SharedFlow or Channel for events |
Mixing LiveData & Flow | Inconsistent updates | Use 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 immutableActions
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 β
Concept | Meaning | Key Benefits |
---|---|---|
SSOT | Single Source of Truth | Data consistency, centralized logic |
UDF | Unidirectional Data Flow | Predictable and traceable state |
Separation of Concerns | UI / Logic / Data decoupled | High cohesion, low coupling |
Immutable State | Read-only data exposure | Prevents 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