[英]MVVM - Having a hard time understanding how to create the Domain layer in Clean Architecture
I'm trying to learn MVVM to make my app's architecture more clean.我正在尝试学习 MVVM 以使我的应用程序架构更加干净。 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.我的
View
是活动。 My ViewModel
has a public method that the activity can call.我的
ViewModel
有一个活动可以调用的公共方法。 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.一旦调用 ViewModel 中的方法,它就会调用我的
Repository
class 中的方法,该方法执行网络调用,然后将数据返回给 ViewModel。 I then update the LiveData
in the ViewModel so the Activity's UI is updated.然后我更新 ViewModel 中的
LiveData
,以便更新 Activity 的 UI。
This is where I'm confused on how to add a Domain
layer to the structure.这是我对如何向结构添加
Domain
层感到困惑的地方。 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.我已经阅读了很多关于域层的 Stackoverflow 答案和博客,它们大多都告诉您从
ViewModel
中删除所有业务逻辑并制作纯 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
?我将从
ViewModel
通信到Domain
class 并且Domain
class 将与Repository
通信?
View --> ViewModel --> Domain --> Repository
I'm using RxJava
to make the call from my ViewModel
to the Repository
class.我正在使用
RxJava
从我的ViewModel
调用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.
正如你所说,你需要 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.假设您有一个与 Detail Viewmodel 通信的 DetailActivity。 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).
因此,您所有的 DetailViewModel 都将知道“Detail Usecase”(调用存储库中的 Detail 方法)。
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
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. PS:用例不是特殊的 android class,您不需要固有任何行为(如片段,活动,视图模型......)它是一个正常的 class 将接收存储库作为参数。
I spent quite a bit of time trying to learn how to add a domain layer using RxJava
by reading a lot of blogs and Stackoverflow answers, but all of them were missing the conversion of the response from the api call to what you'd like to display on screen (For example if the back end returns a username dave123
and you'd like to display by dave123
).我花了很多时间尝试通过阅读大量博客和 Stackoverflow 答案来学习如何使用
RxJava
添加域层,但是所有这些都缺少将 api 调用的响应转换为您想要的响应在屏幕上显示(例如,如果后端返回用户名dave123
并且您希望by dave123
显示)。
I finally figured it out and the secret sauce was to use a RxJava .map()
operator inside the UseCase
class.我终于想通了,秘诀是在
UseCase
class 中使用 RxJava .map()
运算符。 I also decided to keep the RxJava call inside my ViewModel
.我还决定将 RxJava 调用保留在我的
ViewModel
中。
So in my Repository
class I have a method that calls the Api and returns a type of Single<Response>
.因此,在我的
Repository
class 中,我有一个调用 Api 并返回Single<Response>
类型的方法。 This is the raw json data the Api returns.这是 Api 返回的原始 json 数据。
public class MainRepository {
private final MainService service;
private final PostDao postDao;
@Inject
public MainRepository(MainService service, PostDao postDao) {
this.service = service;
this.postDao = postDao;
}
public Single<Response> getResponse() {
return service.getSubredditPosts();
}
}
Inside my GetPostsUseCase
class, I'm call the getResponse()
method from the MainRepository
and altering the Response
by performing business logic on it (the stuff I want to display on the UI. In this case I add the String
"by " to the username)在我的
GetPostsUseCase
class 中,我从MainRepository
调用getResponse()
方法并通过对其执行业务逻辑来更改Response
(我想在 UI 上显示的东西。在这种情况下,我将String
“by”添加到用户名)
And the secret or the part I had alot of trouble understanding/figuring out how to do was converting the Type inside the Single<>
.我在理解/弄清楚如何做时遇到很多麻烦的秘密或部分是在
Single<>
中转换类型。 I used the .map()
operator to change the return type and filter the Response
to a List<Post>
我使用
.map()
运算符更改返回类型并将Response
过滤到List<Post>
public class GetPostsUseCase {
private final MainRepository mainRepository;
@Inject
public GetPostsUseCase(MainRepository mainRepository) {
this.mainRepository = mainRepository;
}
public Single<List<Post>> getSubredditPosts(){
return mainRepository.getResponse().map(response ->
getPostsFromResponse(response.getData().getChildren())
);
}
private List<Post> getPostsFromResponse(List<Child> listChildren) {
List<Post> listPosts = new ArrayList<>();
for (Child child : listChildren) {
Post post = child.getPost();
post.setCreatedBy("by " + post.getUsername());
listPosts.add(post);
}
return listPosts;
}
}
And this is how my ViewModel
looks like这就是我的
ViewModel
的样子
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 GetPostsUseCase getPostsUseCase;
@Inject
public PostViewModel(GetPostsUseCase getPostsUseCase) {
this.getPostsUseCase = getPostsUseCase;
getSubredditPosts();
}
public void getSubredditPosts() {
getPostsUseCase.getSubredditPosts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Post>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
centerProgressLiveData.setValue(true);
}
@Override
public void onSuccess(@NonNull List<Post> list) {
Log.d(TAG, "onNext: Query called");
centerProgressLiveData.setValue(false);
listPosts.clear();
listPosts.addAll(list);
getPostsLiveData.setValue(listPosts);
}
@Override
public void onError(@NonNull Throwable e) {
centerProgressLiveData.setValue(false);
}
});
}
I couldn't find any blogposts or answers that had an example like this.我找不到任何具有此类示例的博文或答案。 Hopefully this helps anyone out there who is struggling to learn how to implement clean architecture with MVVM, Hilt, RXJava and a Domain layer.
希望这可以帮助那些正在努力学习如何使用 MVVM、Hilt、RXJava 和域层实现干净架构的人。
If I did do something incorrectly or not considered clean architecture please let me know.如果我确实做错了什么或不被认为是干净的架构,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.