Implementing Single Source of Truth in Android
Single-source of truth (SSOT) architecture is a software engineering principle that also applies to Android app development. It is defined as, and quoted,
the practice of structuring information models and associated data schemas such that every data element is mastered in only one place, providing data normalization to a canonical form.
- Wikipedia
This article seeks to explain this architecture in relation to Android app development, as well as implement a sample project illustrating this concept.
Why do we need SSOT?
With Android, the primary source of truth is mostly the local database. This local database is usually implemented using Room, SQLite, RealmDB, etc. This approach to architecting an Android app is beneficial because it keeps data in one place, and only the item's reference is passed around in the application. It is also easier to sync and update data from this single source. SSOT ensures data integrity, which refers to data’s overall accuracy, completeness, and consistency. Keeping data in a primary source of truth location ensures that the saved data is consistent and accurate across the app.
Any Android app that provides offline-first functionality needs to ensure the data is kept and made easily accessible. A use case class can be exposed such that any screen interested in accessing the data can have access without much work. If a single object is needed, the identifier for an object can be used as a reference for this object.
The flow chart below illustrates this concept.
Project setup
The project uses the MVVM architectural pattern alongside Room and Flows. It fetches random quotes from an API using Retrofit and saves the quotes in the Room DB. This setup is fairly easy to implement and ensures the extendability of the project, meaning new features can be added, using the same repository, without much overhead.
On Android Studio, create a new project then add the following dependencies.
// okhttp & retrofit
implementation “com.squareup.okhttp3:okhttp:4.10.0”
implementation “com.squareup.retrofit2:retrofit:2.9.0”
implementation “com.squareup.okhttp3:logging-interceptor:4.9.1”
implementation “com.squareup.retrofit2:converter-gson:2.9.0”
// hilt
implementation “com.google.dagger:hilt-android:2.42”
implementation ‘androidx.hilt:hilt-navigation-compose:1.0.0’
kapt “com.google.dagger:hilt-compiler:2.42”
// room db
implementation “androidx.room:room-ktx:2.6.0-alpha02”
implementation “androidx.room:room-runtime:2.6.0-alpha02”
kapt “androidx.room:room-compiler:2.6.0-alpha02”
- OKHttp and Retrofit for fetching data from the remote API.
- Hilt is added for dependency injection.
- Room library provides an abstraction layer over SQLite to allow fluent database access
We proceed to set up the data source classes. We’ll need the remote and local data source files for this project. The remote data source would manage how we fetch data from the API, and the local data source manages how data is fetched from the local database.
Database setup
One of the capabilities of the Room database is the support for observable read queries in the DAO (data access object) class. When the state of the database changes, the database can emit new values that meet the queries' condition. These observables can be Flow<T>
(Kotlin), Flowable, Observable, Publisher
(RxJava), or LiveData
. For this project, we’ll use Kotlin Flow. The flow would emit new quotes as soon as they’re fetched from the API, thereby keeping our database as the single source of truth for the quotes. This ensures we do not update the UI directly from the API but from the database.
Remote data source
Repository setup
Use cases
Use cases make up the domain layer but would be skipped for this tutorial. It is part of the full project available on GitHub. For the project, we set up two use case classes, one for each of the repository functions.
ViewModel
The ViewModel class is part of the presentation layer, and it brings together all these classes and functions. The important functions of this class are the getRandomQuotes()
and fetchRandomQuote()
functions. The first sets up a flow of data and listens to changes in the database. fetchRandomQuote()
is enabled to allow users to add a random quote to the UI.
Note that the if-check in lines 18–24 can be moved down one layer into the use case class. Keeping it this way makes the use case class definitive and contains only one functionality.
Conclusion
The database of an Android application serves as the source of truth for offline-first applications. This ensures data accuracy and correctness. Also, we’ve been able to demonstrate how to implement the SSOT concept with a sample Android application. The application fetches random quotes from an API resource and saves them on a local database. The UI is then populated with the data from this database.
Do reach out in the comment section in case there’s a need for clarification.
Github repo
References
- Random quotes API: https://api.quotable.io/quotes/random
- Wiki: https://en.wikipedia.org/wiki/Single_source_of_truth
- Room Database setup: https://developer.android.com/training/data-storage/room
- Domain layer: https://developer.android.com/topic/architecture/domain-layer