Dsug 05 02-15 - ScalDI - lightweight DI in Scala
-
Upload
ugo-matrangolo -
Category
Software
-
view
4.346 -
download
0
Transcript of Dsug 05 02-15 - ScalDI - lightweight DI in Scala
![Page 2: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/2.jpg)
Inversion Of Control
● The Hollywood Principle "Don't call us, we'll call you"
● You relinquish control on when your code gets executed to an external framework
○ Windowing system○ Callbacks○ High-Order functions
![Page 3: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/3.jpg)
Inversion Of Controldef service(s: String): Future[String] = future {
s.toUpperCase}
val foo = service(“foo”)val FOO = Await.result(foo, Duration.Inf)println(FOO + “ - bar”)
service(“foo”).onSuccess {case res => println(res + “ - bar”)
}Not IoC: full control of program flow
IoC: scala.concurrent._ will run your code
![Page 4: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/4.jpg)
Dependency Injection
● A form of IoC where you relinquish the object creation/initialisation logic
class Manager {val serviceOne = new ServiceOneImpl()val serviceTwo = new ServiceTwoImpl()
def tx() {val id = serviceOne.fetch()serviceTwo.store(id)// ...
}}
object ManagerFactory {val Instance = new Manager(
new ServiceOneImpl(),new ServiceTwoImpl()
)}
class Manager(private val serviceOne: ServiceOne,private val serviceTwo: ServiceTwo
) {def tx() { … }
}
![Page 5: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/5.jpg)
Dependency Injection
● Writing Factories is boring● IoC containers
○ Spring Framework ○ Google Guice ○ Google Dagger○ “The Cake Pattern” ○ ScalDI
![Page 6: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/6.jpg)
The Cake Pattern● Advertised as the Scala way to do DI natively without
any external framework● Based on the concept of “self-type”
○ Constraints an impl of a type to be also an impl of the defined self types
trait Foo {self: Bar with Baz =>// the Foo type is going to assume that it is also a Bar and a Baz// ...
}
![Page 7: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/7.jpg)
The Cake Pattern
trait Shell {}trait Engine {}
trait Car {self: Shell with Engine =>
}
trait RenaultShell extends Shell {}trait NissanEngine extends Engine {}trait NissanShell extends Shell {}
class RenaultScenic extends Car with RenaultShell with NissanEngine {}class NissanQashqai extends Car with NissanShell with NissanEngine {}
![Page 8: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/8.jpg)
The Cake Pattern
● First introduced by Odersky as the main modularization tool for the scalac/scala toolset [1]
● Further evangelized by Boner on his blog [2]● Easy cookbook on the Cake Solution blog [3]
![Page 9: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/9.jpg)
The Cake PatternLet’s use it to build a simple app with this structure:
UserService
UserDao
MongoDB PostGres
![Page 10: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/10.jpg)
The Cake PatternLike:● No need of a IoC framework● All your deps are checked at compile timeDon’t like:● Lots of boilerplate code● On complex layouts is hard to see what the deps of a module
are● Introduces ‘hard’ concepts of the Scala type system● Hard to get it right● All these traits will slow down compilation time
![Page 11: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/11.jpg)
The Cake Pattern
Truth is that people will start to give up on it pretty soon shortcutting to abominations like:
trait MyServiceImpl extends MyService with DependencyAwith DependencyBwith DependencyCwith DependencyDwith DependencyEwith DependencyFwith DependencyG...
![Page 12: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/12.jpg)
The Cake PatternCurrent Production code in GILT:
object Application extends Journal with Autocomplete with ProductLookSearch with SaleSearch with CategorySearch with AgeAndGenderSearch with HealthCheck with RuntimeEnvironment with Prefetch
![Page 13: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/13.jpg)
The Cake Patterntrait RuntimeEnvironment extends Environment override val search = ??? override val searchEventPublisher = ??? override val users = ??? override val sales = ??? override val ruleSets = ??? override val productLooksCache = ??? override val productDetailsCache = ??? override val externalProductsCache = ??? override val taxonomiesCache = ??? override val skuJournalsCache = ??? override val publicBrandsCache = ??? override val ruleSetsCache = ??? override val salesCache = ??? override val brandPromotions = ??? override val personalizer = ??? override val kafkaUtil = ??? override val benefitsCache = ??? override val hopperSales = ??? override val redirectClient = ??? override val preferences: Preferences = ??? // … more !!
Who is depending on what ?
![Page 14: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/14.jpg)
The Cake Patterntrait SaleSearch extends SearchController with ResponseHelper
with RedirectHelper with PilHelper
with HopperHelper with SaleHelper with ExternalProductViewHelper
with PageComponentsHelper with TopOfFunnelHelper with AbTestHelper {
self: Environment =>
// WTF! // Stuff popping out from nowhere}
● On what SaleSearch really depends on?
● We basically flattened the dependency graph!!
![Page 15: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/15.jpg)
The Cake Pattern● Hard to get it right even for Odersky & Co.● Just happened on scala-internals ML:
https://groups.google.com/forum/#!search/scala-internals$20EECOLOR/scala-internals/Z0kV6iDam0c/-09cDMEz754J
“I do have some opinions about the trait you linked. I think it's design is flawed. Take for example it's base definition:
trait Typechecker extends SymbolTable with Printers with Positions with CompilationUnits with QuasiquotesImpl
When I read such a type my mind goes something like this:
So Typechecker is a SymbolTable with Printers and Positions and CompilationUnits and QuasiquotesImpl. That instantly sends me to a place that I don't like….”
“Here we have a peculiar consequence of using the cake pattern. Scalac's codebase illustrates this on a number of occasions as well.”
![Page 16: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/16.jpg)
The Cake Pattern
// in Implicits.scalatrait Implicits { self: Analyzer =>
// in Analyzer.scalatrait Analyzer extends AnyRef with Contexts with Namers with Typers with Infer with Implicits with EtaExpansion with SyntheticMethods with Unapplies with Macros with NamesDefaults with TypeDiagnostics with ContextErrors with StdAttachments with AnalyzerPlugins
![Page 17: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/17.jpg)
The Cake Pattern
● No control over initialization logic○ Is not part of the DI definition but is a nice to have○ With Cake you need to code it yourself○ Could be problematic due to trait linearization
● Let’s see how it could happen ...
![Page 18: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/18.jpg)
ScalDI● IMHO the Cake pattern is
○ too much hassle○ hard to get it right○ badly implemented could cripple your code base
● Alternatives?● Be disciplined and write your factory objects● ScalDI
○ Small and simple○ Easier to use and to understand
![Page 19: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/19.jpg)
ScalDISimple: only 3 concepts● Injector
○ A container for your bindings● Module
○ A place where you define the bindings using a nice DSL
○ It is an Injector● Injectable
○ Provides a DSL to inject dependencies
![Page 20: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/20.jpg)
ScalDISimple example
class DefaultServiceImpl(implicit inj: Injector) extends Service with Injectable { val conf = inject [String] (identified by “service.configuration”) def run() = { println(s”conf: $conf”) }}
class ServiceModule extends Module { binding indentifiedBy “service.configuration” to “jdbc://mysql/mydb” bind [Service] to new DefaultServiceImpl // bind [Service] toNonLazy DefaultServiceImpl }
class MyApp(implicit inj: Injector) extends Injectable {private val service: Service = inject [Service]
}
![Page 21: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/21.jpg)
ScalDIModule composition
val storeModule = new Module {bind [UserDao] to new PsqlUserDaoImpl()
bind [OrderDao] to new MongoDbOrderDaoImpl()}
val cacheModule = new Module {bind [UserCache] to new LRUUserCacheImpl()bind [OrderCache] to new TTLOrderCacheImpl()
}
val appModule = storeModule :: cacheModule// then in test ...val mocks = new Module {
bind [OrderDao] to new MockOrderDaoImpl()}val appModule = mocks :: appModule // will redefine OrderDao with a mock
![Page 22: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/22.jpg)
ScalDIConstructor injection
class ServiceImpl(dao: Dao, cache: Cache) extends Service { // … }
val module = new Module {bind [Service] to injected [ServiceImpl]
}
// after macro expansion
val module = new Module {bind [Service] to new ServiceImpl(
dao = inject[Dao],cache = inject[Cache]
)}
![Page 23: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/23.jpg)
ScalDIYou can inject everything!
val myModule = new Module {binding identifiedBy “greetings” to “Hello World!”binding identifiedBy “adder” to ((a: Int, b: Int) => a + b)
}
class Consumer(implicit inj: Injector) extends Injectable {val greet: String = inject [String] (identifiedBy “greetings”)val adder = inject [(Int, Int) => Int] (identifiedBy “adder”)
}
![Page 24: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/24.jpg)
ScalDIConditional injection
val daoModule = new Module {bind [Dao] when (inDevMode or inTestMode) to new H2Dao()bind [Dao] when to new PsqlDao()
def inDevMode = !inTestMode && !inProdModedef inTestMode = System.getProperty(...)def inProdMode = System.getProperty(...)
}
![Page 25: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/25.jpg)
ScalDILifecycle!
trait Cache { def start()def stop()
}class CacheImpl extends Cache {
def start() = { fetchAll() }def stop() = { … }
}
val app = new Module { // will init all your bindingsbind [Cache] to new CacheImpl()
initWith { _.start() }, destroyWith { _.stop() }
}app.destroy() // will invoke all your destroy logics
![Page 26: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/26.jpg)
ScalDI + PlayCan inject into Play controllers!
// with ScalDI a Controller is not an Object!!// just remember to add a @ in the routes fileclass MyController(implicit inj: Injector) extends Controller with Injectable {
private val serviceOne = inject [ServiceOne]private val serviceTwo = inject [ServiceTwo]
def index = Action {Ok(serviceOne.run(serviceTwo.run()))
}}
![Page 27: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/27.jpg)
ScalDI + AkkaInject Props and ActorRef
class ParentActor(implicit inj: Injector) extends Actor with AkkaInjectable {val childActorProps = injectActorProps [ChildActor]val friendActorRef = injectActorRef [FriendActor]
def receive = {case Spawn => context.actorOf(childActorProps)case Greet => friendActorRef ! Hello
}}
![Page 28: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/28.jpg)
ScalDI + Play = DEMO!
![Page 29: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/29.jpg)
ScalDII like:● Easy● Nice DSL to inject/define dependencies● Different forms of injections● Handles lifecycle● Supports Play and AkkaDon’t like:● Still somewhat intrusive
![Page 30: Dsug 05 02-15 - ScalDI - lightweight DI in Scala](https://reader035.fdocuments.us/reader035/viewer/2022062406/55a77b911a28abc4668b4597/html5/thumbnails/30.jpg)
EOF