Suggestion on creating a module that only exposes a public API

from the CommonsWare Community archives

At March 10, 2020, 3:46am, sagarsuri56 asked:

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


At March 10, 2020, 11:26am, mmurphy replied:

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

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.


At March 10, 2020, 12:04pm, sagarsuri56 replied:

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.


At March 10, 2020, 12:40pm, mmurphy replied:

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.


At March 10, 2020, 5:18pm, sagarsuri56 replied:

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.