I'm trying to improve my app dependencies and came across a problem. My code base is shared with another app, so I have modules that are shared between these two apps. The below image is a simplified version of what I currently have.
Currently, my app build gradle sets the AppNetwork dependency as implementation and the AppNetwork declares the SharedNetwork dependency as api. It's api because app module needs to know stuff about the SharedNetwork module (more on this next). App module also implements the SharedFuncionalities module.
SharedNetwork contains an INetwork interface. This interface is extended by IAppNetwork interface that it's implemented by AppNetworkImpl (the latter two files are placed in the AppNetwork module).
Here is an example of why this is being set like this.
My app module creates AppNetworkImpl and also creates an ItemRepository (location is inside the SharedFuncionalities module) that takes INetwork (implemented by AppNetworkImpl) as a param in its constructor. This allows the ItemRepository to "talk" with the SharedNetwork module.
If I set AppNetwork to declare the SharedNetwork dependency as implementation thus making my app module unaware of the SharedNetwork (that makes sense to me. I would then use AppNetworkImp as the bridge between the app module and the SharedNetwork module) I then lose the ability to create objects in the SharedFuncionalities that use AppNetworkImpl as the bridge for using the SharedNetwork.
What I would like to know if I have the possibility of the app module only knowing about AppNetwork but still create objects in SharedFuncionalities that can "talk" with the SharedNetwork.
Related
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?
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```).
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.
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.
I'm very familiar with MVVM architectural pattern in android. To take it much further, I'm doing my next project by following clean code principles (SOLID). I have separated the entire app in three modules. 1) App (presentation + framework) 2) Data 3) Domain. My doubt is that whether I can keep library dependencies (i.e. Firebase) in Data module or not. Right now, I'm using interface to access app related stuffs like shared preferences, location fetchers, retrofit, etc.
I need to expect values like AuthResult from Data module. For that I need to add Firebase dependencies in the data module's Gradle file. I think that will violate the Higher level module should not depend on lower lever module rule.
Can anyone clarify this for me?
After going through several articles on MVVM + Clean Code, I came to a conclusion that I cannot be using any dependencies related to android framework inside either domain or data module. Otherwise it will be violating the Dependency Inversion principle of SOLID.
Dependency Inversion principle
Higher level module should not be dependent on Lower level modules, and both should be dependent on abstraction.
In English -> You cannot directly access framework related components like database, gps, retrofit, etcetera from data or domain layers. They should not care about those stuffs. They should be totally independent of android related components. We can use interface to satisfy the rule of abstraction.
Therefore, my data module and domain module contains only language dependencies. Whatever android-framework-related data I want, I acquire it by implementing interfaces.
I have a main application which uses Dagger (some previous version) for providing dependencies.
Now, I am writing an SDK where I am using (Dagger 2.10+). Every thing works fine, when I have an application class, as applications has (HasActivityInjector) and it's responsible for the initialization of DaggerAppComponent.
My Question is -
Should I use Dagger 2 in my sdk (I want to as it makes my code more testable)
If not, then I am considering writing my own Injection class.
If I go for it, how should I initialize at the application level as the only solution for this I found out was to have an Application class in sdk and make client extend it. (I dont want this change at client side and not a good design).
Any suggestions would be great!!
I recently had to tackle the same issue: Create an SDK that can be used in any supported Android App.
I did have to accept the premise that some of the of the Apps that would want to use the SDK won't themselves be using Dagger or even dependency injection.
The major issue with creating an SDK that uses dependency injection is, as OP has pointed out, the Application implementation in the App.
Part of the reason behind the creation of the HasActivityInjector, HasFragmentInjector or HasSupportFragmentInjector has been the need to comply with the following dependency injection rule:
A class shouldn’t know anything about how it is injected. Reference A
With the creation of the HasXInjector interfaces, Android allows the the Dagger Dependency Graph to be attached to the Application, and then for the Application to be responsible for injecting the dependencies where they need to be injected through the following code:
AndroidInjection.inject(this) // <-- 'this' being an Activity or Fragment
SDK ISSUE
The issue is that the SDK does not have an Application implementation of its own. Additionally, the SDK can not add anything to the Application implementation of the App, nor can it override the Application implementation of the App.
In the case of an external SDK, it will not even know about an Application implementation, just the Application Interface.
So even if the Application that has included the SDK uses Dagger, which is not guaranteed, the SDK will not be able to add it's Dependency Graph to the Dependency Graph used by App's Application implementation, making all SDK dependencies unreachable.
And if the Application does not use Dagger, how would the dependencies be injected in the first place?
My own solution to this issue has been to break the rule quoted above and not use the HasActivityInjector, HasFragmentInjector or HasSupportFragmentInjector for injection internally in the SDK.
I am creating an instant app, which include application module, base feature module, instant app module and an another feature module. Problem is i am not able to access the activities of application module from base-feature and feature module and same between base-feature module and feature module but i am able to access activity of base-feature module from application module.
Right now i am accessing the activities using :
Intent i = new Intent(this,
Class.forName("com.demo.test.appmodule.TextActivity"));
by this method studio don't show me any errors at compile time.
Is there any other way for communication between two different feature modules?
Why i am able to access base-feature module activity from application module but not vice versa?
can we access activities of application module from base or any other feature module?
Can i have a link which define the project structure for an instant app
Thanks in advance
The reason why you can't communicate directly between features is because they're independent from one another.
The correct way to handle this is calling it with its URL, example: android-instant-apps/hello-feature-module/HelloActivity.java
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://hello-feature.instantappsample.com/goodbye"));
intent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivity(intent);
In an instant-app structure, the base acts as a library for the feature modules, and the features are built into APKs. In an installed-app structure, both the base and features act as libraries for the application module. Some explanation can be found here:
Why use the new Android feature plugin over the library plugin?
Android: Can you add activities to the Instant App module?
There used to be a page # https://g.co/instantapps that explained the structure of instant apps, but looks like it's missing. However, you can take a look at:
https://android-developers.googleblog.com/2017/08/android-instant-apps-best-practices-for.html
bottom of page # codelabs/android-multi-feature-instant-app/#3
And no, you won't be able to directly access activities of the application from a feature. As an installed-app, com.android.feature modules are compiled/behave as com.android.library modules, so apply the same rules here: the application depends on the library, not the other way around. To traverse that direction, you will need to use the same kind of Intent as shown above.
Anything in com.android.application will be isolated from the feature modules of the instant-app, and will only appear in the installed-app.
I'm using working on a project in the CLEAN architecture where the project is broken into the "Presentation", "Domain" and "Data" modules, where the Domain module hosts the "Entities" that are basically the data models specific to this project. An example of this architecture is here.
Unlike the other two module, "Domain" is a pure Java library module, which is great for clarity and testing as it doesn't have the Android overhead, however it also means I'm now not able to use libraries like "Parceler" to which is very Android specific. Is there a way around this?
Parceler allows you to configure beans outside of the given module to generate a wrapping Parcelable via the #ParcelClass annotation. This means you can configure the given bean as a #Parcel outside of the Data layer, and in the presentation layer (or wherever else you want). See http://parceler.org/#classes_without_java_source for specifics.
The org.parceler:parceler-api module is also pure Java, it has no dependencies on the Android api. Therefore you should be free to annotate your Data module without violating the CLEAN archetecture you're seeking. The annotation compiler portion (org.parceler:parceler) of the library, however, does rely on the Android API, so you'll need to run it in the android-specific module. This leaves you with the follow:
Include the parceler-api library in your Data module and annotate your Data layer beans (#Transient, #ParcelProperty, etc). If you don't need any specific configuration, you can avoid including the parceler-api as a dependency.
Add the parceler and parceler-api libraries to your Android-specific module (Presentation?).
Add a #ParcelClass annotation with each class from your data module you want to be an #Parcel to an arbitrary class (Application?). This will direct Parceler to generate a Parcelable for each class identified within the #ParcelClass parameter.