5

Square Inc. has presented it's internal modular architecture at Droidcon SF'19:

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

However, I'm a bit confused with some bullets. Could you please help me?

  1. Why do they actually need :wiring modules? I find it adding complexity:
  • you get extra gradle module for each new feature

  • you have to make a sort of global injection into your Fragments somewhere in :app, because Fragments defined in :impl modules cannot access it's DaggerComponent, which is defined in :impl-wiring modules. :impl doesn't depend on :impl-wiring, because the dependency is reversed.

  • you cannot have an Android Dynamic Feature modules, because they should know about it's DaggerComponent in order to inject it's Fragment. But there is no way to do such injection from :app module, which is base-module for Dynamic Features.

so why :wiring modules at all?

One can merge :impl and :impl-wiring, or :fake and :fake-wiring together to eliminate all the issues mentioned above. And also, in :demo-apps one could just have a dependency on either :impl or :fake``, and not on :impl-wiring(or:fake-wiring```).

Maxim Alov
  • 604
  • 4
  • 14

2 Answers2

0

The creation of this type of modules is to separate even more. With this you generate an abstraction of the type of component you use (koin, dagger) and how. If the project is large, it makes sense to do it.


Currently I generate the following flow of dependencies between modules:

WARNING: Check the directionalities well.

  • :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

I'm still not sure if app should depend on open and impl-wiring, or which app should only depend on open and open from impl-wiring.

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
0

Eventually, I came up with the following solution:

each feature consists of the following gradle-modules:

api

impl and fake

data:api

data:impl1 ... data:implN and data:fake

data:wiring

ui

demo

So, here api, impl and fake as usual, but I've my data layers separated. I bought myself that I need multiple different implementation of data layers sometimes, for example - if I develop Stock-Charts App, I could rely on Finnhub Open API or MBOUM API or provide fake implementation.

Thus I have data:api, data:implX. Indeed, data:api defines FeatureRepository interface (one or many) and data:implX provides actual implementation for them. In order to bind interface and implementation, I use data:wiring, which defines Dagger modules and component(s). In addition, I keep the same package names within each data:implX module in order to "write-once" the data:wiring module. And to replace one implementation with another, I just change a single line in data:wiring/build.gradle which states a sort of:

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

Also, to break the confusion mentioned in my original question, I introduce ui module, which contains some Views of a particular feature. Fragments go in demo (a standalone app to test feature) or in ui, they refer to viewModel which have some bindings ctor-injected from Dagger component of a feature. But the UI and library are separated here. Fragment instantiates a dedicated Dagger component that uses component dependencies to refer to feature's library bindings, such as interactor or repository etc.

So, to wrap up - separation between UI and business logic implementation (a "library") for each feature makes it possible to solve the issue. Feature's api declares an entry point to it's functionality as a library, and it's global access via Dagger multibindings from :app. So it can be used further in any :demo, :ui and :dynamic-feature.

Maxim Alov
  • 604
  • 4
  • 14