Featured image of post Don't Pass Around Your Room/Database Entity

Don't Pass Around Your Room/Database Entity

If you’re thinking of modularizing your Android App and you use Android Room, you should not pass around your Room Entity.

Room: Only In Your Database Module

Room is great, and so is modularization. If you’re using Room, you probably have several Room Entity Objects that look kinda like this:

Chances are you’re passing this model around everywhere. Every time you want to access your User class, or save it, or display the data to the UI, you use the same User class, which doubles as your Room Entity.

For a one-module proof of concept app, this is probably fine. But the moment your app expands and you begin to modularize, you’ll run into issues. Specifically, Gradle will tell you that you have unresolved Supertypes.

Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
com.example.persistence.AppDatabase, unresolved supertypes: androidx.room.RoomDatabase

This happens because you’re passing the Entity from your database module to other modules which don’t implement the room compiler. So then you add the room compiler to your other modules. Just like StackOverflow suggests.

Don’t do this. Please. It breaks the core concept of Information Hiding as would be required by a project that implements a proper Separation of Concerns.

Map Your Entity

Instead, you should have two distinct objects. You should one object that represents the database Entity, and one object for passing around the pure data. Your database module should be literally the only module which imports the Room library. If any other module imports those implementation details of the database module, then you are not Information Hiding as you should. This is especially true if you may want to change out your Room database for something like SqlDelight.

Instead, map your data class to a new Entity, like so:

Perhaps you have some other way of mapping to and from your User to your UserEntity class. You may think it’d be better if it happened automatically via a Serializer or the like. Normally I’d agree that automated serialization is easier in the long term, but this way, any changes to your User class results in a compiler error in your UserEntity class, which shows you:

  1. That your database needs to go up a version; and
  2. what migration steps to take, based on what changes the mapper requires.

I suggest you turn all your database classes into internal class, as I’ve discussed before. This is a bit of a tedious thing to do sometimes, but you will be super-happy in the long term. Future work and refactoring will be very easy.

There is one other thing though.

LiveData + Room, But In Another Module

One of the nice things about LiveData and Room is how easily they interact.

But then you have this problem in your Repository

compiler error

If you see this warning, you're headed in the right direction.

Turns out you can’t use LiveData to hook directly on to your database, because that exposes internal database entities outside the module. You want LiveData but you still want a proper separation of concerns. How? You use the LiveData Transformation function.

This means you have a direct LiveData pipeline from your ViewModel/View to your database, without actually exposing your internal database components. You get to have your module and access it too.

Conclusion

I showed you why you shouldn’t pass around your Room @Entity, and how to separate your modules without losing functionality.

comments powered by Disqus