Suggestion on creating a module that only exposes a public API

I am working on a multi-module android project. I have created a separate module that will have the database(room) implementation in it. I want to only expose an interface that avoids other modules to directly access the room specific APIs so that it will be easy to replace room with SQLDelight in the future. How should I approach this problem where I can limit access to just an interface and not any database specific APIs?
I found protection proxy design pattern can be of help here but still I would like your overview on it.
@mmurphy

That may not be possible. In the end, you will wind up using

  • private in Java and Kotlin
  • package-private in Java
  • internal in Kotlin

as much as you can, to limit what other modules can see. However, Room will have its own requirements. For example, I think entities and DAOs need to be public. That will limit your ability to hide them from other modules.

1 Like

I took the following approach:

  1. I created a separate database module.
  2. Made DAO, Entity and Database as internal.
  3. Created a separate model class(data class) with the same properties as the Entity.
  4. Created an interface which has functions mapping to the DAO functions.
  5. Created an implementation of the above interface which takes database instance as a constructor parameter.
interface UserLocalSource {
    fun addUser(user: User)
    fun getUser(firstName: String): User
    fun deleteUser(user: User)
}

class UserLocalSourceImpl private constructor(private val database: Database) : UserLocalSource {
    override fun addUser(user: User) {
        database.insert(user.toUserEntity())
    }

    override fun getUser(firstName: String): User {
        return database.find(firstName).toUser()
    }

    override fun deleteUser(user: User) {
        database.delete(userEntity = user.toUserEntity())
    }

    companion object {
        fun newInstance(): UserLocalSource {
            return UserLocalSourceImpl(Database())
        }
    }
}

Now the app module can only access UserLocalSource.

That’s certainly a fine plan, even if you were doing this all in one module. And I am glad to hear that Room is more internal-friendly than I was expecting.

Just bear in mind that anything that is public (which is the default visibility in Kotlin) will still be visible in the other module.

1 Like

Yeah that’s right. I have just made sure that no DAO or Entity should be directly accessible instead it should be behind a public API.