You should modularize your Kotlin Android app, since it’s the best way to enforce separation of concerns, but how do you know if you’re doing it right? Here’s some tips to help.
Am I doing it right?
If you’re following one of the great guides out there, you’re probably doing it mostly right, but how do you know? Can you measure a difference? Here’s a good way to check. Generate a dependency graph for your app. I used APK Dependency Graph Generator. All you need to do is compile the apk-dependency project, point it to an apk, and it will generate an interactive graph like the one below.
This graph demonstrates a way to look for ease of refactoring with two nice examples:
- The
ServerClient
in the bottom right provides a complete separation between the api and the rest of the app. The api calls can therefore be refactored easily. - Lots of classes are reliant on the
TeaBag
class in the middle. A single change there is likely to cascade outward, making refactoring difficult.
Below, an example of a project which is not easy to refactor, update or even debug:
There is no separation of concerns here. This project is in dire need of modularization, but where do you even begin?
Where to begin?
I usually follow these steps:
- Create a module
- Identify one or more classes that should not be accessible outside of a specific context
- Move those classes into the module
- Add the
internal
visibility modifier (or making the classpackage-private
if you’re using Java) - Try to compile and get an error
- Fix this error without removing the
internal
keyword. This may involve creating a little bit of boilerplate.
A specific example of this would be the database module. I moved my database
class, data model
classes, DAO
classes, and repository
class into the database module. I made everything internal
except for repository
, which provides the API to the database for the rest of the app. After some restructuring and cleanup, I ended up with a nicer class structure.
Okay, I have to admit, it doesn’t seem like we made that much of a difference. But here’s the real benefit:
The real benefit
When a new developer works in a non-database module and tries to access the database directly, the compiler will stop them.
The two dependency graphs are similar, except that a moment’s thoughtlessness can pull everything into spaghetti in the first instance. I have used the internal
modifier in my database module, which means that a developer would be stopped from breaking a delicate separation of concerns with a single function.
If you run into these errors in a project, you know the project is well modularized.
Conclusion
We have looked at two ways in which to analyze the state of your modularization:
- APK Dependency Graph Generator; and
- The prevalance of the
internal
modifier (or lack ofpublic
in Java)