This is not the commonly asked question about the difference between api
and implementation
, and hopefully will be more advance and interesting from the point of view of architecting a multi-module project.
Let say I have the following modules in an application
library
base
feature1
feature2
app
Now the relations between the modules are:
base
wraps library
feature1
and feature2
make use (depends) on base
app
puts together feature1
and feature2
Everything in this multi module structure should be able to work using Gradle's implementation
dependencies and there's no need to use the api
clause anywhere.
Now, let say feature1
needs to access an implementation detail of base
included in library
.
In order to make library
available to feature1
we have two options as far as I can tell:
Change
implementation
forapi
inbase
to leak the dependency to modules that depend onbase
Add
library
as animplementation
dependency tofeature1
without havingbase
leak the dependency onlibrary
Of course the example has been simplified for the sake of the question, but you understand how this can became a configuration hell with a big number of modules with 4 or 5 levels of dependencies.
We could create a base-feature
intermediate module that can wrap base
and provide another level of abstraction for feature1
to consume without leaking library
, but let's leave that solution out of the scope of this problem to focus on the setup of the dependencies.
Some trade-offs that I detected on the above options:
Option 1) pros
- Smaller
build.gradle
's files, as no need to repeatimplementation
clauses - Faster build scrips edits. Just make the single change on the
api
clause and see the changes propagated to all consumer modules
Option 1) cons
- Classes might come up available in modules that shouldn't have access to them.
- Prone to miss use by developers, as they have implementations available and not only the module interfaces.
Option 2) pros
- It makes crystal clear which dependencies the module has.
- No guessing where the classes are coming from (think 4 or 5 levels of modules leaking dependencies), as their origin is always declared in the module dependencies.
Option 2) cons
- Makes updating a dependency more tedious, as all the modules with the
implementation
clause have to be modified. Even though I believe this is a good thing because it keeps track exactly of how a change modified the project, I see how it can take more time.
Now the questions:
Is there any trade-offs in terms of compilation of this multi-module scenario?
Is a module leaking a dependency "faster" to be compiled for consumer modules?
Does it make a substantial difference in build times?
What other side effects, pros/cons am I missing ?
Thanks for your time.
implementation
in libraries, then in the host application I will be forced to write something likeimplementation library implementation wraps implementation base
Otherwise app will be a crash in runtime. Those, there will be awraps
andbase
leak anyway. If I useapi
in libraries, then in the host application I will be forced to write something likeimplementation library
and it's all. and here will also be your "leak" – Scripture