为什么我们需要将DI布线与Android多模块体系结构中的实现分开?

问题描述

Square Inc.在Droidcon SF'19上展示了其内部模块化体系结构:

https://www.droidcon.com/media-detail?video=380843878

但是,我对子弹有些困惑。你能帮我吗?

  1. 为什么他们实际上需要:wiring模块?我发现它增加了复杂性:
  • 每个新功能都会获得额外的gradle模块

  • 您必须对:app中某个位置的 Fragments 进行某种全局注入,因为在:impl模块中定义了 Fragments 无法访问在:impl-wiring模块中定义的 DaggerComponent :impl不依赖于:impl-wiring,因为依赖关系是相反的。

  • 您不能拥有Android动态功能模块,因为他们应该知道它的 DaggerComponent 以便注入它的 Fragment 。但是无法通过:app模块(动态功能的基本模块)进行这种注入。

那为什么要:wiring个模块呢?

一个人可以将:impl:impl-wiring:fake:fake-wiring合并在一起,以消除上述所有问题。而且,在:demo-apps中,人们可能只依赖于:impl:fake``,and not on :impl-wiring (or:fake-wiring```)。

解决方法

创建这种类型的模块更是为了分离。有了这个,您可以生成您使用的组件类型(koin,dagger)以及如何使用的抽象。如果项目很大,那么做是有意义的。


目前我在模块之间生成以下依赖流:

警告:检查方向性。

  • :feature-A:open <- :feature-A:impl -> :feature-A:impl-wiring
  • :feature-A:impl-wiring -> :feature-A:impl,:feature-A:open
  • :app -> :feature-A:open,:feature-A:impl-wiring

我仍然不确定应用程序是否应该依赖于 open impl-wiring,或者哪个应用程序应该只依赖于 open 中的 openimpl-wiring .

,

最终,我想出了以下解决方案:

每个功能由以下 gradle-modules 组成:

api

implfake

data:api

data:impl1 ... data:implNdata:fake

data:wiring

ui

demo

所以,这里的 apiimplfake 像往常一样,但我的数据层是分开的。我认为有时我需要多种不同的数据层实现,例如 - 如果我开发股票图表应用程序,我可以依赖 Finnhub Open APIMBOUM API 或提供虚假实现。

因此我有data:apidata:implX。实际上,data:api 定义了 FeatureRepository 接口(一个或多个)并且 data:implX 为它们提供了实际的实现。为了绑定接口和实现,我使用了 data:wiring,它定义了 Dagger 模块组件。此外,我在每个 data:implX 模块中保留相同的包名称,以便“一次写入” data:wiring 模块。为了将一个实现替换为另一个实现,我只需更改 data:wiring/build.gradle 中的一行,其中说明了一种:

implementation project(":data:implA")implementation project(":data:implB")

此外,为了打破我在原始问题中提到的混淆,我引入了 ui 模块,其中包含一些特定功能的 Views片段进入demo(一个独立的测试功能的应用程序)或ui中,它们指的是viewModel,其中有一些从Dagger注入的绑定ctor组件。但是 UI 和库在这里是分开的。 Fragment 实例化一个专用的 Dagger 组件,它使用组件依赖来引用功能的库绑定,例如 interactor 或 存储库等

所以,总结一下 - 每个功能的 UI 和业务逻辑实现(一个“库”)之间的分离使得解决这个问题成为可能。功能的 api 将其功能的入口点声明为库,并且通过来自 :app 的 Dagger multibindings 进行全局访问。因此它可以在任何 :demo:ui:dynamic-feature 中进一步使用。