Building Features by Independent Dagger Components

Applications grow so fast! Especially when there are lots of people working on them, and lots of feature to build upon. Trying to keep the architecture as clean as possible is always the biggest challenge in this case.

I have published a concept work about how to keep your Application class decoupled to reduce application’s awareness about the dependencies.

And with this concept I will explain; how you can decouple your Features’ Dagger implementations from each other and let them have their own world. Reduce dependencies between core and features, so that you can build uni-directional dependency and have clean separation.

Source: The Daily Task

Complications

  • Most of the Applications have their one big main component to hold instances tied to Application lifecycle.
  • That one big component is initiated on the first launch of the Application even though you don’t really need all the modules to be initialized.
  • Features have to define their Feature specific modules in that big component, even though the rest of the application (other features) shouldn’t care or know about those.
  • At some point, that gets out of control with lots of modules and components to maintain.
  • And the most crucial is this creates circular dependency between core & features or / and even between features.
Application depends on all features and vice versa

Diagram Disclaimer: Please notice Application Scope means modules are meant to be live as long as Application lifecycle, Feature Specific Scope can be any custom scope that has different lifecycle.

With this approach, Application scoped component lives in application.

  • In order to build that component; every feature which wants to keep their object in Application Scope must be a dependency on application so that they can provide modules to that component.
  • Feature specific components would need to depend on Application Component to build themself.

This creates circular dependency and prevents us to split features into separate modules while their modules live in their own environment.

P.S.: This only shows the first layer of the dependency, when it grows it gets more and more complex / dependent.

Solution

  • Let every feature have their own Application lifecycle component.
  • Let there be a core component that every feature can depend on, so the whole Application can use some common instances as well.
  • Do not create all of the modules or components if user will never visit that feature. Generate only when the component is needed.
  • Application is not aware of any feature, and works as proxy so that it just distributes the components.

Result

Clear separation between features, Modularization.

Every feature as its own application

With this approach; Every feature will have their own Application scoped component, which depends on core Application component.

  • No circular dependency
  • No module is provided to upstream
  • Feature specific code stays in feature
  • Application and Features are unaware of each other

How to Apply the solution?

I have tried out 2 different approach and created sample implementations.

By defining contract in base and implementing on higher level module

You can create some interfaces and in base module create your Application class with some abstract methods that return this interface and override them on top level app module that you combine all of them.

Every Feature will be still able to create and maintain their own application scoped component and modules, but Application will know about which feature needs what component and how to generate/provide them.

By using a more generic approach to have even clearer separation

With this approach you can get your Application completely unaware of which feature needs what component and how to generate them.

All will be collected within the library by reading through the manifest file and every feature will have their completely separated/isolated modules.

I will not go to details about the library on this article, but for more detailed explanation about how to use it, please check the README in the repo.


Let me know what you think about the implementations and share your opinions about the problem. Don’t forget to mention which path you follow to solve it.


Gave this as talk in MobileOptimized2018 — you can watch it here.