giso

    giso

    1 year ago
    Hi there, We have several classes in multiple Gradle modules, where we would like to make the constructor only internally visible to protect against accidental couplings. However most of our DI-wiring happens inside the main application module and therefore, so we are forced to make the visibility
    public
    . I was wondering how this use case can be fulfilled with Kotlin. Maybe with a compiler plugin, which is only applied to the main module, granting higher visibility? Or more tightly scoped with a method annotation granting only this method a higher visibility? What are you thoughts, or have I overlooked an already present solution?
    t

    tmg

    1 year ago
    I don't think that is an issue the language should tackle. You don't need to do the wiring all on the same file, and if a component should just be
    internal
    then why do you need to wire it outside of the module? whatever depends on it should also be inside that very same module.
    regarding this:
    Or more tightly scoped with a method annotation granting only this method a higher visibility?
    if there is such things as
    @VisibleForTesting
    there could also be
    @VisibleForWiring
    I guess.
    wasyl

    wasyl

    1 year ago
    What kind of DI do you use? In java internal=public so it kind of circumvents that visibility. It’d be a super hacky approach and not very future-proof (e.g. Dagger might move to KSP in the future) unless you write everything manually. But would work, kind of
    giso

    giso

    1 year ago
    We use Koin and our entire code base is Kotlin-only. Never looked back once 😉
    The problem with wiring inside the module is, that the module does not have and should not have dependencies on some of the implementation details of the class dependencies. This would violate the dependency inversion principle.
    wasyl

    wasyl

    1 year ago
    I have similar dillema, overall, but for now we use Dagger which actually hid the issue for us. What you can consider is having the concrete types public, but move wiring to an intermediate module which will only depend on the concrete classes as
    implementation
    and the API for that (interfaces) as
    api
    . So you’d expose implementations to the wiring module, but in
    app
    (or elsewhere) you’d depend not on the implementation module, but on the wiring module
    giso

    giso

    1 year ago
    We also considered a similar approach, extracting the interface and moving the implementation to a higher layer, only usable by the app module. But for some small classes, creating an extra module seems pretty much overkill.
    Also some colleagues argue, that small functionalities should be kept together in one module and not be scattered between layers, making it harder to navigate.
    There is also already something similar, but slightly different to what we need:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-published-api/
    wasyl

    wasyl

    1 year ago
    If it’s safety you’re after, I suppose a compiler plugin/custom lint check + Tiago’s
    @WiringOnly
    or
    @VisibleForWiring
    annotation solution is the most reasonable
    giso

    giso

    1 year ago
    It’s more expressiveness I am after 😉 But a compiler plugin allowing the module it is applied to, access to annotated classes/functions sounds reasonable. I’ll look into it. Thanks for your input 😊
    rnett

    rnett

    1 year ago
    Another option is to make and use a
    RequiresOptIn
    annotation. It's more documentation than a hard forbid, but it should prevent inadvertent usages.
    giso

    giso

    1 year ago
    Nice idea! And the best part is, it comes almost for free.