https://kotlinlang.org logo
#dagger
Title
# dagger
r

ritesh

03/11/2020, 7:41 PM
Recently working on a project and followed modularization with clean architecture. db-module -> all db related stuff and dagger provider, the class with @Module annotated api-module -> all api related i/o's and dagger provider domain-module-> dependent on above 2 using
impl
not
api
as i dont want to trigger app-module build if any change in above to modules. And it has use cases and Dagger @Module class with above @Modules added (data and api) Something like this
Copy code
@Module(includes = [ApiModule::class, DatabaseModule::class])
object AppModule
app-module -> the ui layer, dependent on domain module using
impl
not api, and this module has Dagger components. Problem starts when dagger kicks in, whe dagger builds the graph it generated all the factories in the generated folder for providers in the sepecific modules like for ApiModule in its own and DatabaseModule in it's own and they are only exposed in domain layer and domain layer in app layer by impl. After compilation dagger throws AppComponent not found. The issue i found was it was not able to reach the generated factor classes from app-layer as app-layer is dependent on domain and domain on data and api-layer using
impl
not
api
and changin to
api
fixed by issue. But, i dont want to go this way, because if any changes in the outer layers (data and api) the build will also be trigerred for app-layer if exposed by api. I am thinking of keeping the all providers classes in the domain-layer. I think it will work. Am I breaking any rules or there is a better way to do it.
m

mateusz.kwiecinski

03/12/2020, 7:24 AM
If you’re trying to follow clean architecture then
domain-module-> dependent on above 2 using
doesn’t sound right. Domain supposed to be independent, framework agnostic entity. It’s
db-module
and
api-module
which should depend on
domain-module
After you inverse those dependencies you’ll find out that you don’t have a place which ties all components together. (which is the
domain
in your current state) The solution is to separate app configuration from ui layer. To have a
presentation/ui
module which would hold the ui and separate
app
which would contain application configuration. That should be the only module with flavours and buildtypes (as they define app configuration, right). In such case the
ui-module
depends on domain (as every other data layer module in clean architecture context) and the app has reference to all of them. With such setup you can have a kotlin-only domain module (no need to add Android Framework dependency there) which makes sure that you don’t use any external dependencies. That may raise your earlier concern about:
i dont want to trigger app-module build if any change in above to modules.
but as you can see, app module becames a tiny one, with almost no code. The most resource demanding
ui
doesn’t get rebuild, as expected. In your case you’ll have to rebuild
domain
which became the largest module each time you modify data layer class.
I am thinking of keeping the all providers classes in the domain-layer. I think it will work.
That kind of solution hase a few downsides, mainly, you won’t be able to declare
db
and
api
classes visibility as internal. There shouldn’t be a need to expose them publicly. I’d expect that they implement interfaces declared in the domain and only those are exposed (from a module) using a DI framework. After you migrated your project to follow above pattern you might start considering what would happen if you started generating all DI classes within each module. Right now you’re using `@Module`s which generate providers, but the whole DI graph is still calculated within a single module which needs to have all dependencies (the issue you faced initially). With suggested approach, as the
app
becames the configuration holder, you can simply put
compileOnly "some-requred-dependency"
without worying someon will consume them in you UI code. Although, there is an alternative way to have more decoupled graph calcutaion and that could be achieved by exposin `@Component`s from each module, but that can be done only after all module dependencies are oriented in a proper way.
r

ritesh

03/12/2020, 9:32 AM
Thanks for the response @mateusz.kwiecinski
Domain supposed to be independent, framework agnostic entity.
It’s
db-module
and
api-module
which should depend on
domain-module
I think since domain layer is the central layer, it can be dependent on the data layer, and provide the contract to data-layer, as in what it wants and data layer will implement. And all the business logic will reside in the
use-cases
inside the domain layer. Since modularization is in picture, and my ui-layer is dependent on the domain , and domain on data (local and remote). I dont want to expose through api, and leverage incremental build process.
I am thinking of keeping the all providers classes in the domain-layer. I think it will work.
I was thinking earlier on doing the same, but it won't serve the purpose and not-ideal. having all generated classes there. I am not sure if this with dagger, that it's compiler is not modified to support
implemented
vs
api
m

mateusz.kwiecinski

03/12/2020, 9:55 AM
is the central layer, it can be dependent on the data layer.
It cannot if you're trying to follow clean architecture. Otherwise sure, you can try other patterns. And I'd argue if in such case domain can be called central layer. It's just in the middle and it happens only because you have simple project structure.
it's compiler is not modified to support
I'm not sure if that's dagger limitation. I'd rather point out at Gradle and how they isolate different classpaths (behavior introduced in gradle 5.0), but basically that's desired behavior. You shouldn't have data layer dependencies in any ui module classpath.
r

ritesh

03/12/2020, 11:06 AM
And I'd argue if in such case domain can be called central layer. It's just in the middle and it happens only because you have simple project structure.
Yeah, I agree with you here as there is no such rule, if domain should be the central layer, i have designed it as the middle layer which is only kotlin-lib to be unaware of any android stuff and consumes data from the outer layer and gives them contract/rules via interface and business logic via interactors (here its use-cases) and ui-layer will be consuming domain and use-cases. Any change in data, let's say remote or local, it wont affect the domain or ui and modularity and testable ofcourse. Not. a big project though so, i am okay with these 3 layers. My only issue is dagger here, not able to find the generated codes, which is in app-layer, where dagger component are build. To find them i need to expose the db and api layer using
api
which i dont want.
I'm not sure if that's dagger limitation. I'd rather point out at Gradle and how they isolate different classpaths (behavior introduced in gradle 5.0), but basically that's desired behavior.
yeah I am not sure at this part. I was more hoping for dagger to generate the providers based on android module scope, which i am not if it's possible or i am making if any sense.
j

Javier

03/13/2020, 1:25 PM
I think the same, domain should implement no modules, data an UI should implements domain.
r

ritesh

03/15/2020, 1:01 PM
yeah. that's how it should be. Thanks @Javier @mateusz.kwiecinski
9 Views