Android paging library with clean architecture - android

I was trying the paging library from Android Architecture Component but I have doubts integrating it in a clean architecture based project.
Generally I have 3 modules:
Main Module (App)
Data Module (Android module with network and db dependencies)
Domain Module (Pure Kotlin Module)
In order to introduce pagination, I had to consider PagedList<T> class as a domain class. (IMO is not a terrible idea since in the end is a list and the data source is abstracted)
So in the domain layer I can have a repostiory like:
interface ItemRepository {
fun getItems():PagedList<Item>
}
And then in the data module create the implementation like this:
class ItemRepositoryImpl: ItemRepositoy(private val factory:ItemDataSourceFavtory) {
fun getItems():PagedList<Item> {
val pageConfigurations = PagedList.Config.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(15)
.setPrefetchDistance(5)
.setEnablePlaceholders(false)
.build()
return RxPagedListBuilder(locationDataSourceFactory, pageConfigurations)
.setInitialLoadKey(1)
.buildObservable()
}
So far so good. My doubt is when we need to transform the domains model for the presentation layer (let's say my item needs to be aware if was checked to show a checked Icon) normally I would map my domain model into a presentation one.
Im aware DataSourceFactory has map and mapByPage methods, but the factory resides in the data layer. My Viewmodel consumes data from the model layer which in this cases would be a PagedList, and as far as I know, paged list doesn´t support mapping.
So what would be the appropiate thing to do in this situation?

You cannot map PagedList into presentation model, since PagedListAdapter need the PagedList to load next/previous pages.
The PagedList have two main function, firstly it's a data structure to hold a List of items that are paged (Part present and part absent), using snapshot() you can easily get the present items, map domain model into a presentation one and pass it to the ListAdapter to show list of items.
Secondly it has to alert DataSource when more pages are needed. PagedListAdapter receive a PagedList, and upon binding items it depend on loadAround() method of PagedList to detect when new pages are needed.
DISCLAIMER: This is just my opinion and open to discussion
but PagingLibrary is not a clean solution by default, the easiest way is to map domain model inside DataStore upon fetching them (either network or db) and pass the PagedList with the presentation models to the presentation layer. Mapping PagedList itself is not logical (though possible) as it is tightly coupled with PagedListAdapter in View.

Related

In Clean architecture MVVM where transform objects to adapter?

So, let's suppose I have an adapter with multiples view types and what I receive from server api is just a list and I have to transform in my adapter objects.
Example:
I have a list of persons that have a type associated, let's think 1 and 2, and I want to show in my recyclerview an header between different types.
So, I have a fragment - view model - use case - repository (request to api)
My adapter expect a lista of persons.
Let's think Person is a interface, and person1 and person2, implements that.
Final, I receive from server a list with objects PersonApi, that have a type as parameter and I have to convert that to my adapter objects.
Where I should do this transformation?
Repository?
Use case?
View model?
My guess, should be on use case.
Edit:
Added a chart
Let's start with the fact that the full implementation of clean architecture has lots of layers, model mappings, ... which makes it hard to implement. So, we can take advantage of a simplified version, just like you mentioned in your question in three layers; Data, Domain and Presentation.
It is nice to have specific data models for each layer, but it's also ok to share Domain models with Presentation as well. It means that you need just one step of data mapping, which happens between Data and Domain right at the adjacency point.
Since the repository is the bridge between Data and Domain, it is the right place to map models.
Definition of repositories should be part of Domain, and the implementation inside Data. This way Data layer provides exactly what Domain layer expects. Keep in mind that Domain MUST NOT know anything of Data layer and its implementation. So, if you are implementing your layers in different gradle modules, you should have a dependency to Domain inside Data module, and not the other way round.
A simplified implementation of classes could be like the following:
Domain:
interface Person
class Person1() : Person
class Person2() : Person
interface PersonRepository {
fun getPersons(): List<Person>
}
class GetPersonsUseCase(val repository: PersonRepository) {
fun execute(): List<Person> {
return repository.getPersons()
}
}
Data:
class PersonRepositoryImpl(val service: PersonService): PersonRepository {
fun getPersons(): List<Person> {
val list: List<PersonEntity> = service.getPersons()
return list.map { it.toPerson() }
}
}
PersonEntity.toPerson(): Person {
return ... // create an instance of Person1 or Person2 based on requirements.
}
As per the basic rule for Clean architecture that every layer is completely independent from other layer. so here you have to write business logic either in Model class or View model class based on the scenario, publish this unique attribute value to view(adapter). Please find below the high level architecture diagram:
You can make a mapper class to transform data in your use case and return domain object into your view
more information and sample code
Do it in Use Case layer, all of case the ViewModel shouldn't change any data from API, it only send it to view.
In the repository you change the response to your Domain Entity, inside the ViewModel you change the Domain entity to View Entity, where the Domain is where your behavior is controlled for example you fetch data from API and then present in the view layer and you would like to add certain attributes for the view you map the domain to view and then if you would like to store things in database you map from view to domain and from domain to Database Entity

Is it proper to use ViewModel for the ListView and the <T> in the ListView?

Preamble
In trying to get my head around the Kotlin classes to implement Android's ViewModel (and MVVM pattern) as used with Fragments and Activities, it is not clear to me of the trade-offs among the various complex classes especially how they have inherited implicit operations and visible methods (e.g., from the observer objects, managed scope, etc.) versus the old O-O approach of passing list-items and lists between activities in an intent as a bundle or reference, etc.
To illustrate my learning dilemma, I am implementing a crunchy cookie and and a jar to contain the cookies. The cookies can be created, consumed and viewed inside the cookie jar.
Android code tends to be vague on details of classes and the tutorials use deprecated versions, so it is difficult to follow best-practices with the latest version of the Android Architecture Component libraries.
Pseudo Kotlin code:
data class CrunchieCookie : {
var flavor: String?
var calories: String?
var photo: ImageView?
}
class CrunchieCookieViewModel : ViewModel() {
val _crunchieCookie: CrunchieCookie?
val crunchieCookie: CrunchieCookie = _crunchieCookie
}
class CookieJarListViewModel: ViewModel() {
val _cookieJar: MutableLiveData<CrunchieCookie>?
val cookieJar: LiveData<CrunchieCookie> = _cookieJar
}
Purpose
I am expecting to create, update and destroy crunchie-cookies
I am expecting to put crunchie-cookies in a cookie-jar (and take them out)
I am expecting to list all the crunchie-cookies in the cookie-jar in a scrolling ListView
I am expecting to click on a crunchie-cooking in the cookie-jar to open an detail view of the cookie
Finally, storing the cookie-jar in a remote DB, so planning for the local/remote data-source in the future
So, to my way of thinking, the cookie viewmodel will be used in CRUD operations and reused in the detail view from the list model.
MAKING #Tenfour04 's COMMENT AN ANSWER.
Your ViewModel should have a LiveData<List>. The Fragment containing the ListView should observe the LiveData for changes and pass the List along to the ListView when the LiveData value changes. If you're actually just modifying the contents of a MutableList, then you need to set the value of the MutableLiveData to that same list to inform it that there's a change it needs to notify observers about. – Tenfour04 Sep 9 at 0:02

Why ViewModel's object shouldn't manipulate database directly?

I am learning Android Architecture Components.
For exemple, and be more easier to understand, If i want to build a TO DO LIST app, my item creation DAO should be
#Dao
public interface ItemDao {
#Insert
long insertItem(Item item);
}
and my viewModel could be use this DAO to insert an item in my TODO list.
But, in architecture component, it is recommanded to NOT manipulate the database by the viewmodel but by the repository.
So, the code should be like that
public class ItemDataRepository {
private final ItemDao itemDao;
public ItemDataRepository(ItemDao itemDao) { this.itemDao = itemDao; }
// --- CREATE ---
public void createItem(Item item){ itemDao.insertItem(item); }
It seems redundant when we cannot understand why.
My question is : why?
I use the Repository for a couple of reasons:
Separation of concern I let the repo be responsible for downloading and storing all the data. That way the ViewModel doesn't have to know any specifics about where the data is coming from, e.g. if it's from an API or a cache. It also makes it easier to write Unit tests for the ViewModel, since all the database and API logic should already be tested in Unit tests for the Repository.
Reusability Lets say you fetch the data from an API and store in a database. If you put the code in the ViewModel and then want to perform the same actions from another place in the app, you need to copy paste the code. By having it in a Repository you can easily share the implementation.
Share data If you have multiple screens that show parts of the same data set, having one Repository passed around between the screens make it easy to share the data. No more trying to pass large amount of data in Bundle and Intent.
Lifecycle Let's say you download data from an API to show in your view. If you fetch the data in the ViewModel you will have to re-download it when the screen is closed and re-opened, since the ViewModel is discarded. However if you store it in the Repository it can use the Application lifecycle, so when you revisit the screen again, the data is already in the cache.

How to add new items to a pagedList backed by Room?

I am using Network + Database for pagination in my app. I have 2 model classes that extends same types. Only one type of model is backed by a Room database. I want to inject other model class in between the PagedList based on some business rules. When I try to do that by using mapByPage function on DataSource.Factory returned by Room. The paging library is throwing IllegalStateExeception with message that size has changed. How do I go about implementing this case?
I think the size of the list needs to be the same. I tried to look for official documentation but I couldn't find it.
But found this comment
https://stackoverflow.com/a/49666673/430652

MVVM - Where to load data from internet

I'm trying to use this MVVM for my android app. I've done many apps, but I'm trying to step up on another level and trying to use the MVVM. But I need to understand where I should load the data from internet. I'm using RxJava, but I'm not sure if I should load data only in ViewModel. If so then where do I set the data. I'm using Databinding from google, but I don't wanna set data in xml through viewModel. I want to set it from the java file.
I'm sorry if I miswrote something, post an answer and I will try to fill out any required informations.
"Triggering a DataLoad" is part of presentation logic. Hence, this should belong in ViewModel.
Details about "How data is loaded" for example, networking logic, does not belong to the ViewModel layer. I highly recommend using Retrofit as you are already using RxJava.
As rx.Observableand databinding.ObservableField are very similar, you can convert them from one form to another. I have written a library that allows you to do this. See FieldUtils.java for an implementation.
Either ways, assuming you have a DataService interface/class:
public interface DataService {
Observable<String> loadSomeData();
}
you can build your ViewModel as follows:
public class ExampleViewModel {
ObservableField<String> title;
public ExampleViewModel(DataService dataService) {
this.title = FieldUtils.toField(dataService.loadSomeData());
}
}
Then, you can display this in your View using Data Binding syntax
<TextView
android:text="#{viewModel.title}" />
I recently blogged about using RxJava with MVVM. I showed an app which loads a list of events from Github using Retrofit and displays them in a RecyclerView. This has been implemented in MVVM.
Article link:
MVVM using RxJava + Data Binding example: Loading data using Retrofit
A more complicated example which also shows a loading indicator and error: DataLoadingViewModel.java.
There is two similar architectural patterns - MVP and MVVM. The main difference is that in MVP parttern Presenter desides how to display data, but in MVVM pattern View receives Model and renders itself (takes data from model). Classical MVVM example is view bindig. But the point is - nomatter what pattern you use, you should obtain all data in Model - and place all your business logic in Model too.

Categories

Resources