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

MVVM - Having a hard time understanding how to create the Domain layer in Clean Architecture

我正在尝试学习 MVVM 以使我的应用程序的架构更加简洁。但是我很难掌握如何为我的应用程序创建“域”层。

目前我的项目结构是这样的:

我的View是activity。我的 ViewModel 有一个 activity 可以调用的 public 方法。一旦调用 ViewModel 中的方法,它就会调用我的 Repository class 中的方法,该方法执行网络调用,然后 returns 将数据返回给 ViewModel。然后,我更新 ViewModel 中的 LiveData,以便更新 Activity 的 UI。

这是我对如何向结构添加 Domain 层感到困惑的地方。我已经阅读了很多关于域层的 Whosebug 答案和博客,它们大多都告诉您从 ViewModel 中删除所有业务逻辑并制作一个纯 Java/Kotlin class.

所以不用

View --> ViewModel --> Repository

我会从 ViewModelDomain class 通信,而 Domain class 会与 Repository 通信?

View --> ViewModel --> Domain --> Repository

我正在使用 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();
    }
}

有人可以给我举个例子说明我该怎么做吗?我在这里迷路了

我在尝试找出域层时遇到了困难。 最常见的例子是用例。

您的视图模型不会直接与存储库通信。正如你所说,你需要viewmodel》domain》repository

您可能会认为用例是对每个存储库方法的抽象。

假设您有一个 Movies Repository,您可以在其中为电影列表调用一个方法,为电影详细信息调用另一个方法,为相关电影调用第三个方法。

每个方法都有一个用例。

它的目的是什么?

假设您有一个与 Detail Viewmodel 通信的 DetailActivity。您的视图模型不需要知道所有存储库(在详细信息屏幕上调用电影列表方法的目的是什么?)。因此,您的 DetailViewModel 所知道的只是“Detail Usecase”(调用存储库中的 Detail 方法)。

Google几小时前更新了架构文档,快来看看吧! https://android-developers.googleblog.com/2021/12/rebuilding-our-guide-to-app-architecture.html?m=1&s=09

PS:用例不是特殊的 android class,您不需要固有任何行为(如片段、activity、视图模型...)这是一个正常的 class 将接收存储库作为参数。

你会得到类似的东西:

视图模型:

function createPost(post Post){
   createUseCase.create(post)
}

用例

function createPost(post Post): Response {
   return repository.create(post)
}

我花了很多时间尝试通过阅读大量博客和 Whosebug 答案来学习如何使用 RxJava 添加域层,但是所有这些都缺少响应的转换api 调用您想要在屏幕上显示的内容(例如,如果后端 return 是用户名 dave123 而您想要显示 by dave123)。

我终于想通了,秘诀是在 UseCase class 中使用 RxJava .map() 运算符。我还决定将 RxJava 调用保留在我的 ViewModel.

所以在我的 Repository class 中,我有一个调用 Api 和 return 类型 Single<Response> 的方法。这是 Api return 的原始 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();
    }
}

在我的 GetPostsUseCase class 中,我从 MainRepository 调用 getResponse() 方法并通过对其执行业务逻辑来更改 Response (我想在 UI 上显示的内容。在这种情况下,我将 String“by”添加到用户名)

我遇到很多麻烦的秘密或部分 understanding/figuring 是如何转换 Single<> 中的类型。我使用 .map() 运算符更改 return 类型并将 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;
    }
}

这就是我的 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);
                    }
                });
    }

我找不到任何包含此类示例的博文或答案。希望这对正在努力学习如何使用 MVVM、Hilt、RXJava 和域层实现干净架构的任何人有所帮助。

如果我确实做错了什么或认为架构不干净,请告诉我。