softwaremill / macwire

Zero-cost, compile-time, type-safe dependency injection library.
https://softwaremill.com/open-source/
Apache License 2.0
1.28k stars 76 forks source link

Accessing wired instances within modules defined as classes #132

Open blacktooth opened 6 years ago

blacktooth commented 6 years ago

I am new to Scala and Macwire and trying to figure out how to access instances that are wired using modules organized as classes. If modules are defined as traits, one can extend them and access the wired instances in them as below,

trait UserModule {
  lazy val userFinder: UserFinder = wire[UserFinder]
}

class UserFinderSpec extends FlatSpec with UserModule {
 assert userFinder.find(...) should ...
}

If my modules are organized as classes with composition, how do I access the instances?

class UserFinder(databaseConnection: databaseConnection) {
}

@Module
class DatabaseConnectionModule {
   lazy val databaseConnection: DatabaseConnection = ...
}

@Module
class UserModule(databaseConnectionModule: DatabaseConnectionModule) {
    lazy val userFinder:UserFinder = wire[UserFinder]
}
class UserFinderSpec extends FlatSpec {
    //How to access UserFinder here?
    //val wired = wiredInModule(new UserModule(new DatabaseConnectionModule))
    //val userFinder = wired.lookup(classOf[UserFinder]
    //Above way to access the instances seems complicated 
    //as I need to instantiate the modules with it's dependency chain which can be very long in my use-case.
}
adamw commented 6 years ago

Well, if you have an instance of UserModule, simply userModule.userFinder?

blacktooth commented 6 years ago

Well, if you have an instance of UserModule, simply userModule.userFinder?

Getting an instance of UserModule might involve instantiating multiple other modules which can become very long. Something like,

new UserModule(new DataBaseConnectionModule(new HttpConnectionModule(new URLModule(), new ThrottlingLimitsModule(), new TimeoutsModule()))))
adamw commented 6 years ago

Yes, well, that might need a second level of wiring :) But you need to create those instances at some point ...

blacktooth commented 6 years ago

Have you considered how Guice solves this problem using the install method? One can define a module that combines everything,

class DatabaseConnectionModule {
  install(new URLModule())
  install(new TimeoutsModule())
  install(new ThrottlingModule())

  val databaseConnection = ...
}

class UserModule {
  install(new DataBaseConnectionModule())
  val userFinder: UserFinder = ...
}

class AppModule {
    install(new UserModule())
    install(new BillingModule())
}
val wired = wiredInModule(new AppModule())

val userFinder: UserFinder = wired.lookup(classOf[UserFinder])

What is your opinion on this?

adamw commented 6 years ago

I think you can do the same using a module which uses wire to wire other modules, with compile-time safety.