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
Related
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
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.
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.
I have read this impressive article on data modeling in android with clean architecture and MVP.
Now i would like to refactor some existing models that i have in my domain so that they for one do not contain parcelable code (android code) and are streamlined to work in the specific view. You know sometimes we have to alter the model so that it works in a view like for example to get the selected position in a RecyclerView we would add a field called "selectedPosition" to the model. There are many times we need to change the model and then we end up with models that are not pure and a little hard to maintain.
Specifically i have 3 model data for 3 payment systems i am using. All the fields in the 3 models come back different. they have different names for the fields. Can someone show me an example of the architecture used to make all 3 models use a common model ?
Data model
I am sure that your 3 models for 3 payment systems have common features. So you can take this features and put it to interface. Each of your models must implement this interface. In your case it should display a data model.
For example:
class Info {
int id;
String cardNumber;
.......
}
interface ITransactionable { //or abstract class with the common func and prop
void addUserWithCard(String cardNumber, String name);
boolean makeTransaction(\*some params*\);
Info getPaymentUserInfo();
}
class Model1/2/3 implements ITransactionable {
\*In each case methods doing different job but give you same result,
you can take info for your models from servers, clouds, db...*\
}
Domain model
Domain model represent your business logic, manipulating of your data model.
class DonationService {
ITransactionable paymentService;
DonationService(ITransactionable paymentService) {
this.paymentService = paymentService
}
Info makeDonation(int money) {
paymentService.addUserWithCard("4546546545454546", "Vasya");
paymentService.makeTransaction(money);
return paymentService.getPaymentUserInfo();
}
........
}
Each layer must give to next something like API.
Presentation
For example can fill recyclerView with data of each transaction. And take events from view like get detail info about transaction or make new transaction.
You can check this for watching how it can be realized: https://github.com/android10/Android-CleanArchitecture
I'm learning MVP structure and I trying to figure out:
How to pass data between models?
Each model represents one action and if I need to send data from one
model to another one, how can I properly make it? Should I pass data
through presenter, like
firstModel -> commonPresenter -> secondModel
OR
send data between models, like
firstModel -> secondModel?
And what if these models interact with different presenters?
The Model View Presenter pattern, like most architectural patterns, is quite open to experimentation. The important thing to keep in mind is to separate the View from the domain logic and data handling.
Particularly in Android, it's useful to keep the code as far as possible from Framework specific classes like Activities and Fragments.
In my experience, it's best to let Models communicate amongst themselves, as Observers of each other. The same goes for Views and Presenters, or, in general, any component in the same architectural layer.
AFAIK you have to convert your Domain models to your Ui models in presenter so you have to call a method or a constructor like this in presenter:
ModelOne modelOne = new ModelOne(modelTwo);
please consider DRY principals and do NOT assign each field in presenter itself like this
modelOne.title = modelTwo.title;
modelOne.id = modelTwo.id;
but if you want convert a Domain layer model to another Domain layer model you have to do it in other layers. read this for more info:
http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
"the presenter communicates with model layer, converts the data to UI friendly format, and updates the view"
also you can find above sentence in this link:
http://iyadagha.com/using-mvp-ios-swift/