A simple trick to hide internal code from a public Dagger module

In this post I will refer to two different type of modules: Dagger modules and Gradle modules. When you find module it means Gradle module, unless specified otherwise.

One of the peculiarities of Kotlin is the internal visibility modifier.

When I started using this language some years ago, I got the idea that internal mapped to Java’s package-private. I don’t remember exactly when, how, or why, but eventually I realized that this was false.

internal in Kotlin means that the target function/class/property is visible only inside the module it is declared in. A module, as the official documentation says , is:

  • an IntelliJ IDEA module
  • a Maven project
  • a Gradle source set
  • a set of files compiled with one invocation of the <kotlinc> Ant task

With the push for multi-module apps, this modifier is becoming more and more useful for us Android developers. It allows us to define code that is effectively public inside the module, but not visible outside, something that was not possible in Java.

The problem

Among the different challenges arising from multi-module projects, there’s the one about the Dagger setup: how should we structure Dagger code? There are multiple opinions around the topic and even Google has one .

Here I don’t want to discuss these opinions, but rather focus on a very specific roadblock that we may face, i.e. hiding internal dependencies from a public Dagger module.

Let’s say we have, in our project, a repository module that contains the logic to handle some dogs data. 🐶 The other modules don’t need to know how this data is retrieved or stored, they should only care that this module is able to do so. Hence we only want to expose a DogRepository interface.

This is what Kotlin’s internal was born for, so we use it to mark the implementation class: DogRepositoryImpl.

Now we need to bind the implementation to the interface, so that Dagger knows that DogRepositoryImpl is to be injected when DogRepository is needed. We write

This module has to be public because it needs to be added to our Dagger component, that likely resides in the app module:

If we try to compile this code, the compilation obviously fails. DogRepositoryModule is public, but it’s exposing the internal DogRepositoryImpl.

The first time I faced this issue I didn’t immediately figure out the solution. I was puzzled trying to understand what was I supposed to do, because I didn’t want to expose the internal implementation, but Dagger was not cooperating. 👀

The trick is actually super simple and consists in moving the binding to an internal Dagger module InternalDogRepositoryModule, that the public DogRepositoryModule includes. In code:

Aaaaand that’s it folks, we run the app and this time everything compiles!
A simple and easy trick that maybe can help someone who is as lost as I was the first time I faced this issue.