问题描述
Square Inc.在Droidcon SF'19上展示了其内部模块化体系结构:
https://www.droidcon.com/media-detail?video=380843878
但是,我对子弹有些困惑。你能帮我吗?
- 为什么他们实际上需要
: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
中的 open
和 impl-wiring
.
最终,我想出了以下解决方案:
每个功能由以下 gradle-modules 组成:
api
impl
和 fake
data:api
data:impl1
... data:implN
和 data:fake
data:wiring
ui
demo
所以,这里的 api
、impl
和 fake
像往常一样,但我的数据层是分开的。我认为有时我需要多种不同的数据层实现,例如 - 如果我开发股票图表应用程序,我可以依赖 Finnhub Open API 或 MBOUM API 或提供虚假实现。
因此我有data:api
,data: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
中进一步使用。