简体   繁体   English

MVVM - 很难理解如何在 Clean Architecture 中创建域层

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM