the no-framework scala dependency injection framework

43
11/12/2013 BuildStuff 2013 @adamwarski Adam Warski BuildStuff 2013 The no-framework Scala Dependency Injection framework

Upload: adam-warski

Post on 29-Nov-2014

4.857 views

Category:

Technology


0 download

DESCRIPTION

Using a DI framework/container may seem obvious. But when was the last time you considered *why* do you really need one? After all, "dependency injection" is just a fancy name for passing arguments to a constructor. In the talk we'll walk through some of the features of DI containers and see if we can replace them with pure Scala code. We'll start with "manual" DI, followed with using MacWire to generate the wiring code for us. Then we'll proceed to a no-framework scopes implementation (e. g. request or session), which are very useful in web applications. We will also discuss possibilities of adding interceptors using macros. And finally, we'll see how to use traits to create and compose modules (similar to the module concept known from Guice), which can be viewed as a simplified cake pattern. As MacWire heavily uses macros, as a bonus, I'll explain how Scala Macros work and when they can be useful.

TRANSCRIPT

Page 1: The no-framework Scala Dependency Injection Framework

11/12/2013 BuildStuff 2013 @adamwarskiAdam Warski

BuildStuff 2013

The no-framework Scala Dependency Injection

framework

Page 2: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

You don’t need anything special to do Dependency Injection

Page 3: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

We often over-complicate

Page 4: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Who Am I?

❖ Day: coding @ SoftwareMill!

❖ Afternoon: playgrounds, Duplos, etc.!

❖ Evenings: blogging, open-source!

❖ Original author of Hibernate Envers!

❖ ElasticMQ, Veripacks, MacWire!

❖ http://www.warski.org

Page 5: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

What is Dependency Injection?

Page 6: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

What is DI?

class PresentPackager {!

! def wrap() {!

! ! new RibbonSelector().selectRandom()!

! ! …!

} !

}

Page 7: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

What is DI?

class PresentPackager(rs: RibbonSelector) {!

! def wrap() {!

! ! rs.selectRandom()!

! ! …!

} !

}

Page 8: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Yes, DI is just using parameters

Page 9: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Why?❖ Restrict the knowledge of the class

class PresentPackager ! (rs: RibbonSelector) { ! def wrap() { ! ! rs.selectRandom() ! ! … ! } }

class PresentPackager { ! def wrap() { ! ! new RibbonSelector() ! ! ! .selectRandom() ! ! … ! } }

Page 10: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

But still …

❖ We need to have the news somewhere

Page 11: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Let’s create a DI container! a.k.a. framework

Page 12: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

DI in Java

❖ Many frameworks!

❖ Configuration via:!

❖ XML!

❖ annotations!

❖ Java

Page 13: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

What’s wrong with that?

❖ Do I really need a DI framework?

Page 14: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Let’s go back …

❖ … and just use our host language!

❖ in this case, Scala!

❖ mapping DI framework concepts to native language constructs

Page 15: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Manual DI!!

object PresentWrapper extends App {!

! val ribbonSelector = ! ! ! ! new RibbonSelector()!

! val wrappingPaperFeeder = ! ! ! ! new WrappingPaperFeeder()!

! val presentPackager = ! ! ! ! new PresentPackager( ! ! ! ! ! ribbonSelector, ! ! ! ! ! wrappingPaperFeeder)!

}

Page 16: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Manual DI!!

object PresentWrapper extends App {!

! lazy val ribbonSelector = ! ! ! ! new RibbonSelector()!

! lazy val wrappingPaperFeeder = ! ! ! ! new WrappingPaperFeeder()!

! lazy val presentPackager = ! ! ! ! new PresentPackager( ! ! ! ! ! ribbonSelector, ! ! ! ! ! wrappingPaperFeeder)!

}

Page 17: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

MacWireimport com.softwaremill.macwire.MacwireMacros._!

object PresentWrapper extends App {!

! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!

! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!

! lazy val presentPackager = ! ! ! ! wire[PresentPackager]

}

Page 18: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Side-note: Scala Macros

Page 19: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Side-note: Scala Macros

❖ Scala code executed at compile time!

❖ Operate on trees!

❖ Can inspect the environment, generate code!

❖ the code is type-checked

Page 20: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Side-note: Scala Macros❖ E.g. debug macro!

def debug(params: Any*) = macro debug_impl!

def debug_impl ! ! ! (c: Context)! ! ! (params: c.Expr[Any]*): c.Expr[Unit]!

!

debug(presentCount) ⟹!

! println(“presentCount = “ + presentCount)

Page 21: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Side-note: Scala Macros

❖ Debug macro implementation!

import c.universe._!

val paramRep = show(param.tree) val paramRepTree = Literal(Constant(paramRep)) val paramRepExpr = c.Expr[String](paramRepTree)!

reify { println( ! paramRepExpr.splice + ! " = " + ! param.splice) }

Page 22: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Side-note: Scala Macros

❖ MacWire!

def wire[T] = macro wire_impl[T]!

def wire_impl ! ! ! [T: c.WeakTypeTag]! ! ! (c: Context): c.Expr[T]

Page 23: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

MacWireimport com.softwaremill.macwire.MacwireMacros._!

object PresentWrapper extends App {!

! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!

! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!

! lazy val presentPackager = ! ! ! ! wire[PresentPackager]}

Page 24: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Scopes

❖ How long will an object (instance) live?

Page 25: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Singleton & dependent

object NorthPole extends App {!

! // Singleton!

lazy val santaClaus = wire[SantaClaus]!

!

! // Dependent!

def gnome = wire[Gnome]!

}

Page 26: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Arbitrary scopestrait WebFrontEnd { ! lazy val loggedInUser = ! ! session(new LoggedInUser) ! def session: Scope }!

!

trait Scope { ! def apply(factory: => T): T }

Page 27: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Arbitrary scopes

object MyApp extends WebFrontEnd { ! val session: Scope = ! ! ! ! new ThreadLocalScope()!

! val filter = new ScopeFilter(session) ! // bootstrap the web server ! // using the filter}

Page 28: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Arbitrary scopes

class ScopeFilter(sessionScope: ThreadLocalScope) ! ! ! extends Filter {!

! def doFilter(request: ServletRequest) { ! ! sessionScope ! ! ! .withStorage(request.getSession()) {!

! ! ! request.proceed()!

! ! } ! } }

Page 29: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modules

❖ Pre-wired!

❖ Composable!

❖ Dependencies!

❖ Module per package?!

❖ Veripacks :)

Page 30: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modules

❖ Module: trait!

❖ Pre-wired: new, MacWire!

❖ Composable: extends/with!

❖ Dependencies: extends/with / abstract members

Page 31: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modules

trait PresentWrapper {!

! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!

! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!

lazy val presentPackager = ! ! ! ! wire[PresentPackager]!

}

Page 32: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modules

trait PresentFactory extends PresentWrapper {!

! lazy val teddyBearProvider = ! ! ! ! wire[TeddyBearProvider]!

! lazy val toyTrainProvider = ! ! ! ! wire[ToyTrainProvider]!

lazy val presentAssembly = ! ! ! ! wire[PresentAssembly]!

}

Page 33: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modules

trait HomeOfSanta {!

! lazy val santaClaus = wire[SantaClaus]!

! lazy val rudolf = wire[Rudolf]!

! lazy val fireplace = wire[Fireplace]!

!

def presentAssembly: PresentAssembly!

}

Page 34: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Modulestrait PresentWrapper { … } trait PresentFactory extends PresentWrapper { } trait HomeOfSanta { … }!

!

object NorthPole ! extends PresentWrapper ! with PresentFactory ! with HomeOfSanta {!

! santaClaus.deliver()!

}

Page 35: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Testing Santa’s Homeclass HomeOfSantaTest extends FlatSpec {!

! it should “deliver presents” in {!

! ! val mockPresentAssembly = …!

! ! new HomeOfSanta {!

!! lazy val presentAssembly = ! ! ! ! mockPresentAssembly }!

! ! …!

! }!

}

Page 36: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Cake Patterntrait PresentPackagerModule {!

! class PresentPackager { ! ! def wrap() { ! ! ! ribbonSelector.selectRandom() ! ! ! … ! ! } ! }

! lazy val presentPackager = new PresentPackager() ! def ribbonSelector: RibbonSelector }

Page 37: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Cake Pattern

val cake = new PresentPackagerModule ! with RibbonSelectorModule ! with WrappingPaperFeederModule ! with TeddyBearProviderModule ! with ToyTrainProviderModule ! with PresentAssemblyModule ! with … { }

Page 38: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Other features

❖ Interceptors!

!

trait Chimney {!

! lazy val presentTransferer = ! ! transactional(wire[PresentTransferer])!

! def transactional: Interceptor!

}

Page 39: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Other features

❖ Factories!

❖ a dedicated object or …!

!

trait PresentBoxer {!

! def box(size: Size) = wire[Box]!

}

Page 40: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Other features

❖ Instance maps!

❖ for integrating e.g. with Play!

❖ Factories!

❖ In-method wiring!

❖ More coming, someday :)

Page 41: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Summing up

❖ Reconsider using a framework!

❖ Native Scala gives a lot of power!

❖ use it!

❖ wisely!

❖ More flexibility (less constraints)

Page 42: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Links

❖ http://www.warski.org!

❖ https://github.com/adamw/macwire!

❖ http://springsource.com/

Page 43: The no-framework Scala Dependency Injection Framework

@adamwarski11/12/2013 BuildStuff 2013

Thanks!

❖ Questions?!

❖ Stickers ->!

[email protected]