Fire Up Your App With Firebase Firestore in MVVM Architecture With Jetpack Compose (Part III)
Domain Layer Bridge between Data and Presentation
A key feature of the Domain layer is that it must not know about the Data and Presentation layers, but the other layers may know about it. The Domain layer serves as a crucial bridge between the Data and Presentation layers. Why do we need this bridge?
Because the design considerations that drive “How?” we shape data are different for “When?“we want store data and “When?“we want to work with data in our app. That means we need to maintain two sets of data models and the code that maps between them. So far, we have seen the storage side of the story. Now let us look at the domain side.
Data Models Define data models for other layers
In our screen, we want to present a ToC, which is a list of chapters. Here is what the model for a single chapter looks like:
data class ChapterInfo(
val name: String,
val title: String,
val description: String
)
Compare with the model ChapterInfoFire
in the Data layer’s package “remote”. These models are very alike.
The domain model for a quiz is as follows:
data class Quiz(
val questionNumber: Int,
val question: String,
val answer: String,
val image: String?,
val youTubeVideoId: String,
val timestamp: Int,
val chapterKey: String,
val isActive: Boolean
)
Here, we employ a questionNumber
attribute to facilitate both the ranking and unique identification of questions within a chapter or module. The quiz question
may have an explanatory image
to accompany the answer
. The quiz includes a YouTube video identified by youTubeVideoId
with explanatory content. A timestamp
marks the start time of relevant content on the video track. The chapterKey
attribute is employed as a reference to the parent chapter or module, enhancing the ease of navigating associated information.
In the realm of relational databases, the navigation of data relationships across tables is guided by foreign key constraints, ensuring a structured approach. However, Firestore’s NoSQL design opts for a more flexible approach by discarding these constraints to reduce upfront storage design complexity. Nevertheless, this flexibility comes at the cost of requiring additional logic in our application code to navigate these relationships. For intricate relationship scenarios, it may be beneficial to explore alternatives, such as leveraging a “built-to-suit” backend solution with technologies like PostgreSQL and a Python FastAPI wrapper to streamline complex data interactions.
Both the data models we have shown (and any others we need in the future) are housed in package “model” in Domain layer.
Conclusion Takeaways and Next Steps
We have defined the data models in the Domain layer. Our “Android Ally” quiz app does not have complex processing logic at this stage and the models sufficiently structure data for presentation. Where should the logic of mapping between sets of models reside? For this, we have package “mapper” in the Data layer. Let’s look at that next.