Android MVVM ViewModel and Repositories for each entity? - android

With the Android Architecture components and the MVVM pattern I have some question.
Based on most examples around the web there are usually simple examples.
Have an entity for Room
#Entity
public class User{
...
}
Have a DAO
#Dao
public interface UserDao{
...
}
Have a repository
public class UserRepository{
}
ViewModel
public class UsersListViewModel extends AndroidViewModel{
....
}
Now let's extend this and beside user have user_access and user_actions for instance, so have 3 tables.
Questions:
For each table in Room I create entities. Should I have 3 Dao one for each entity (userDao, userAccessDao, userActionsDao) or just a general AppDao class?
Same goes for Repository. One repository for the entire app or Repositories for each Entitiy (RepositoryUser, RepositoryUserAccess, RepositoryUserActions?
If my app has one main activity and multiple fragments, should I create one ViewModel for each fragment?

1
You should have contextual DAOs, let's say an UserDao which should contains the queries related to the users, if you have posts in your app, you should have a PostDao for everything related to posts.
2
Same logic for repositories, remember the Single Responsibility Principle for classes, sticking to that principle you should have repositories for each kind of entities separated (UserRepository, PostRepository...).
3
Following all the new concepts described as Jetpack you should have one viewmodel per fragment, unless for one strange reason you have two fragments that need the exact same logic, and that is very unlikely to happen since the objective of a fragment is to be reused.

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

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 does it work: passing an abstract class as parameter in Room Database builder

I have seen this implementation in Room database.
There is an abstract class AppDatabase -
#Database(entities = {Task.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract TaskDao taskDao();
}
But when you create an Object of this AppDatabase class, you do something like this -
AppDatabase appDatabase =
Room.databaseBuilder(context, AppDatabase.class, "something").build();
My questions are -
How can you pass an Abstract class directly like this without the Override methods defined ?
Like, normally we don't use and pass abstract classes like that, if you do something like this in IDE, it throws an error.
Which design pattern is this and how Room handles this type of things internally ?
1) The idea of using abstract classes is to have a sort of contract between the developer and room. We use abstract classes (or interfaces) because the implementation of those Dao methods will not be provided by us the developers but by Room itself.
2) It is a Builder design pattern, this design pattern is generally used when we have many options for how we'd like to create our final object and the pattern provides a more maintainable api for doing so. The example you provide is just a basic initialization of the database, but we can actually set many params whilst building the database class. For instance we can add the following option when building the database in order to tell it to delete everything and start again in case our database schema changes:
.fallbackToDestructiveMigration()
How Rooms handles thing internally is a bit of a hard question, but in general terms it is an abstraction layer of SQL apis provided by android itself, it will use your contracts (abstract classes or interfaces) for the Daos and the Database in order to create implementations for all of those abstract methods defined in those classes. Once you've setup everything and built your project the first time, Room will generate a bunch of _Impl classes that will implement those abstract methods. For instance, if you have a UserDao, it will generate a UserDao_Impl class that extends (or implements if you've used an interface) the original UserDao and it will provide those implementations. What it does internally will depend on the method, but it's basically using the SQLite api provided by Android.

Single or multiple DAO and Repository in Android Room database project?

I have a Room database project which has a DAO and a Repository (mediator between different data sources) for each table in the database. It is a lot files and class names to remember.
I would like to know if there a disadvantages for using a single Repository and DAO class per project?
There is no such rule that you have to make separate #Dao for every table. You can make one Dao class that holds all of your database queries.
#Dao
interface MyDao{
#Query("SELECT * FROM Student")
fun getStudents(): List<User>
#Query("SELECT * FROM Professors")
fun getProfs(): List<User>
}
But just with the convention, you make separate Dao for every entity. This way your code will be organized, that's all. It's difficult to review your code if you have bunch of unrelated queries within the same Dao.
For repositories, I would have different approach. Although there is no such rule that you have to use separate Repository or single Repository, I would use both wherever I think the one is necessary.
For example, if I have only one database call from User table and one from Post table, I don't see point of having two separate repositories for each table. So I would just make single repository in a such case. But on the other hand, if I have 10 database calls from User table and 10 more from Post table, I would split repositories and have two repositories one for each table.
All in all, these are just conventions and above are just suggested style of coding.

Room database inefficiency

just got started with android room database an I like the efficiency it brings to the android team and the general programming experince. But currently facing some efficiency issues .
my issue is that for a class marked with #database annotation we are recqured to pass all the enties inside the annotation as google explains https://developer.android.com/training/data-storage/room/
#Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
so what if I have like over 50 entity classes and I want to easen the process of passing the classes my Database class is there any option for me ?? I happen to come from a spring background and I like the similarity between the two though in spring there is no such a condition but everything works smoothly
so what if I have like over 50 entity classes and I want to easen the process of passing the classes my Database class is there any option for me ?
Room cannot make assumptions about which RoomDatabase the visible #Entity classes belong to. For example, WorkManager might use Room, and if it does, the WorkManager entities belong to its database, not yours.
You could write some sort of code generator that uses your own personal rules to generate that list of classes, if you wanted.

Categories

Resources