I'm trying to learn MVVM to make my app's architecture more clean. But I'm having a hard time grasping how to create a "Domain" layer for my app.
Currently this is how the structure of my project is looking:
My View
is the activity. My ViewModel
has a public method that the activity can call. Once the method in the ViewModel is called, it calls a method in my Repository
class which performs a network call, which then returns the data back to the ViewModel. I then update the LiveData
in the ViewModel so the Activity's UI is updated.
This is where I'm confused on how to add a Domain
layer to the structure. I've read a lot of Stackoverflow answers and blogs about the Domain layer and they mostly all tell you to remove all the business logic from the ViewModel
and make a pure Java/Kotlin class.
So instead of
View --> ViewModel --> Repository
I would be communicating from the ViewModel
to the Domain
class and the Domain
class would communicate with the Repository
?
View --> ViewModel --> Domain --> Repository
I'm using RxJava
to make the call from my ViewModel
to the Repository
class.
@HiltViewModel
public class PostViewModel extends ViewModel {
private static final String TAG = "PostViewModel";
private final List<Post> listPosts = new ArrayList<>();
private final MutableLiveData<List<Post>> getPostsLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> centerProgressLiveData = new MutableLiveData<>();
private final MainRepository repository;
@Inject
public PostViewModel(MainRepository repository) {
this.repository = repository;
getSubredditPosts();
}
public void getSubredditPosts() {
repository.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
@Override
public void onNext(@NonNull Response response) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(response.getData().getChildren());
getPostsLiveData.setValue(listPosts);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError: getPosts", e);
centerProgressLiveData.setValue(false);
}
@Override
public void onComplete() {
}
});
}
public class MainRepository {
private final MainService service;
@Inject
public MainRepository(MainService service) {
this.service = service;
}
public Observable<Response> getSubredditPosts() {
return service.getSubredditPosts();
}
}
Could someone please give me an example of how I could do it? I'm quite lost here
I had a hard time while trying to figure out the domain layer. The most common example of it is the use case.
Your viewmodel won't communicate directly to the repository. As you said, you need viewmodel 》domain 》repository.
You may think of a usecase as a abstraction for every repository method.
Let's say you have a Movies Repository where you call a method for a movie list, another method for movie details and a third method for related movies.
You'll have a usecase for every single method.
What's the purpose of it?
Let's say you have a DetailActivity that communicate with a Detail Viewmodel. Your viewmodel doesn't need to know all the repository (what's the purpose of calling a movie list method on you Detail screen?). So, all your DetailViewModel will know is "Detail Usecase " (that calls the Detail method in repository).
Google has updated the architecture documentation few hours ago, take a look! https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html?m=1&s=09
PS: Usecase is not a special android class, you do not need to inherent any behavior (as fragment, activity, viewmodel...) it's a normal class that will receive the repository as parameter.
You'll have something like:
Viewmodel:
function createPost(post Post){
createUseCase.create(post)
}
UseCase
function createPost(post Post): Response {
return repository.create(post)
}