SlideShare ist ein Scribd-Unternehmen logo
1 von 99
Downloaden Sie, um offline zu lesen
Typesafe dependency
injection in reactive
applications
Grzegorz Wilaszek
ScaLAB, 25.06.2016
About me
Typesafe DI in Akka
• Problems with asynchronous dependencies
• Need for special DI technique
• Refactoring with type level programming
Dependencies problem
User
ShopId
Future
Shop
Future
Future
Basket
PromotionsFuture
Basket
Actor
ask
response
Products
Introducing an Actor
User
ShopId
Future
Shop
Future
Future
Basket
PromotionsFuture
Basket
Actor
ask
response
Products
Introducing an Actor
User
ShopId
Future
Shop
Future
Future
Basket
PromotionsFuture
Basket
Actor
ask
response
Products
(User,	
  Shop)	
  =>	
  Future[Basket]
(ShopId)	
  =>	
  Future[Shop]
(Shop)	
  =>	
  Future[Promotions]
Introducing an Actor
User
ShopId
Future
Shop
Future
Future
Basket
PromotionsFuture
Basket
Actor
ask
response
Products
findBasket
findShop findPromotions
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class ConfigurablePriceCalculator(user: User, shopId: ShopId,

findShop: ShopId => Future[Shop],

findBasket: (User, Shop) => Future[Basket],

findPromotions: Shop => Future[Promotions],

basketKeeper: ActorRef) extends Actor {



private var promotions: Promotions = _



override def preStart(): Unit = {

super.preStart()

for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promo <- findPromotions(shop)

} yield {

promotions = promo

self ! basket

}

}



override def receive: Receive = waitForBasket



def waitForBasket: Receive = {

case basket: Basket =>

basketKeeper ! AskForProducts(basket)

context become waitForProducts

}



def waitForProducts: Receive = {

case products: Products => context become work(products)

}



def work(products: Products): Receive = {

case Calculate => sender ! promotions.price(products)

}

}
class PriceCalculator(products: Products, promotions: Promotions) extends Actor {

override def receive: Receive = {

case Calculate => sender ! promotions.price(products)

}

}

Desired actor
Refactoring out
dependencies
for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promotions <- findPromotions(shop)

products <- (basketKeeper ? AskForProducts(basket))
.mapTo[Products]


} yield PriceCalculator.props(products, promotions)

Introducing proxy to create
actor immediately
ProxyProps(for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promotions <- findPromotions(shop)

products <- (basketKeeper ? AskForProducts(basket))
.mapTo[Products]

} yield PriceCalculator.props(products, promotions))

Introducing proxy to create
actor immediately
ProxyProps(for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promotions <- findPromotions(shop)

products <- (basketKeeper ? AskForProducts(basket))
.mapTo[Products]

} yield PriceCalculator.props(products, promotions))

monadic futures
Introducing proxy to create
actor immediately
ProxyProps(for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promotions <- findPromotions(shop)

products <- (basketKeeper ? AskForProducts(basket))
.mapTo[Products]

} yield PriceCalculator.props(products, promotions))

Let’s imagine better API
ProxyProps(for {

shop <- findShop(shopId)

basket <- findBasket(user, shop)

promotions <- findPromotions(shop)

products <- (basketKeeper ? AskForProducts(basket))
.mapTo[Products]

} yield PriceCalculator.props(products, promotions))

Let’s imagine better API
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(findShop)

.requires(findBasket)

.requires(findPromotions)

.requires(basketKeeper,
AskForProducts(_),
classOf[Products]))



Let’s imagine better API
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(FutureDependency(findShop))

.requires(FutureDependency(findBasket))

.requires(FutureDependency(findPromotions))

.requires(ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])))



Let’s imagine better API


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



What should we expect?
• Checking availability of dependencies in
compile time
• Efficient composition of futures
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



()
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



()
(ShopId)
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



()
(ShopId, User)
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



()
(ShopId, User)
(ShopId, User, Shop)
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



()
(ShopId, User)
(ShopId, User, Shop)
(ShopId, User, Shop, Basket)
(ShopId, User, Shop, Basket, Promotions)
(ShopId, User, Shop, Basket, Promotions, Products)
Introducing Shapeless -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

















The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head













The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head













The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head



val text: String = hList.tail.head









The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head



val text: String = hList.tail.head









The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head



val text: String = hList.tail.head



val numList: List[Int] = hList.tail.tail.head





The basic concept -
heterogenous lists


val hList = 1 :: "text" :: List(1, 2, 3) :: HNil



val num: Int = hList.head



val text: String = hList.tail.head



val numList: List[Int] = hList.tail.tail.head



hList.tail.tail.tail.head

Recursive type definition
sealed trait HList extends Product with Serializable



final case class ::[+H, +T <: HList](head : H, tail : T) extends HList {

override def toString = head+" :: "+tail.toString

}



sealed trait HNil extends HList {

def ::[H](h : H) = shapeless.::(h, this)

override def toString = "HNil"

}



case object HNil extends HNil


val hList: ::[Int, ::[String, ::[List[Int], HNil]]] =
1 :: "text" :: List(1, 2, 3) :: HNil

val hList: Int :: String :: List[Int] :: HNil =
1 :: "text" :: List(1, 2, 3) :: HNil

Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil]
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil]
ProxyProps[Promotions :: Products :: HNil]
Defining types


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props: Props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId).given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))



Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil]
ProxyProps[Promotions :: Products :: HNil]
Defining types


val shopDependency: FutureDependency[Tuple1[ShopId], Shop] =
FutureDependency(findShop)
val basketDependency: FutureDependency[(User, Shop), Basket] =
FutureDependency(findBasket)
val promotionsDependency: FutureDependency[Tuple1[Shop], Promotions] =
FutureDependency(findPromotions)
val productsDependency: ActorDependency[Tuple1[Basket], Products] =
ActorDependency(basketKeeper, AskForProducts(_), classOf[Products])




Defining types


val shopDependency: FutureDependency[Tuple1[ShopId], Shop] =
FutureDependency(findShop)
val basketDependency: FutureDependency[(User, Shop), Basket] =
FutureDependency(findBasket)
val promotionsDependency: FutureDependency[Tuple1[Shop], Promotions] =
FutureDependency(findPromotions)
val productsDependency: FutureDependency[Tuple1[Basket], Products] =
ActorDependency(basketKeeper, AskForProducts(_), classOf[Products])




Building computation of
Dependencies
Dependencies()
HNil
Building computation of
Dependencies
Dependencies().given(user)
Future[User] :: HNil
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
Future[ShopId] :: Future[User] :: HNil
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Shapeless allows to use tuple (ShopId) as ShopId :: HNil
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Shapeless allows to use tuple (ShopId) as ShopId :: HNil
Shop mustn't be present in dependencies
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Shapeless allows to use tuple (ShopId) as ShopId :: HNil
Shop mustn't be present in dependencies
Future[ShopId] must be found in dependencies
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[ShopId], Shop]
Shapeless allows to use tuple (ShopId) as ShopId :: HNil
Shop mustn't be present in dependencies
Future[ShopId] must be found in dependencies
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[(User, Shop), Basket]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[(User, Shop), Basket]
Future[User] :: Future[Shop] :: HNil
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[(User, Shop), Basket]
Future[User] :: Future[Shop] :: HNil => Future[User :: Shop :: HNil]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[(User, Shop), Basket]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[Shop], Promotions]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[Shop], Promotions]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] ::
Future[User] :: HNil
FutureDependency[Tuple1[Shop], Promotions]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency)
Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] ::
Future[User] :: HNil
FutureDependency[Tuple1[Basket], Products]
Building computation of
Dependencies
Dependencies().given(user).given(shopId)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency)
Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] ::
Future[ShopId] :: Future[User] :: HNil
FutureDependency[Tuple1[Basket], Products]
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps = ProxyProps(PriceCalculator.props)
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
proxyProps(dependencies)
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
proxyProps(dependencies)
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
val props: Props = proxyProps(dependencies)
Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
val props: Props = proxyProps(dependencies)
context.actorOf(props)
Proxy behaviour
• runs all dependencies as one Future
• creates target actor once Future completes
• can retry running dependencies Future
• can re-run dependencies once target actor
terminates
Benefits of using Shapeless
• Clear and concise API
• Reusable dependencies definitions
• Efficient asynchronous computation - composing
futures
• Compiler ensures presence of all required
dependencies
Explaining Shapeless
Recursive implicit lookup
trait NotIn[T, L <: HList]



object NotIn {

implicit def notInHNil[T] = new NotIn[T, HNil] {}



implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H],
hIsNotT: <:!<[H, T],
notInTail: NotIn[T, L]) =

new NotIn[T, H :: L] {}

}
Recursive implicit lookup
trait NotIn[T, L <: HList]



object NotIn {

implicit def notInHNil[T] = new NotIn[T, HNil] {}



implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H],
hIsNotT: <:!<[H, T],
notInTail: NotIn[T, L]) =

new NotIn[T, H :: L] {}

}
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)

addUnique(1 :: "txt" :: HNil, 2)

Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)


Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]


?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]


?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]


?
?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]


?
?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]


?
?
?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]


?
?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]


?
Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 

t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]


Implicits usage in Shapeless
• Stating facts about types
• Often recursive:
• stop condition
• general condition
• Compiler finds implicit recursively
• Value resolved implicitly may have some functions -
the functions can do something useful for given types
More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]

More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]

More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]

More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]

def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL])
: removeAll.Out = removeAll(l)

More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil

val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]

def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL])
: removeAll.Out = removeAll(l)

trait RemoveAll[L <: HList, SL <: HList] {
type Out

def apply(t: L): Out
}
Recap


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))
context.actorOf(props)



Declarative and functional
asynchronous dependencies

val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))
context.actorOf(props)



Type driven composition


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))
context.actorOf(props)



Seamless futures
composition

val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))
context.actorOf(props)



Compile time checking


val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)

.requires(shopDependency)

.requires(basketDependency)

.requires(promotionsDependency)

.requires(productsDependency))
context.actorOf(props)



Less boilerplate
class
findShop: ShopId => Future[Shop],
findBasket: (User, Shop) => Future[Basket],
findPromotions: Shop => Future[Promotions],
basketKeeper: ActorRef)


private var


override def
super
for
shop <- findShop(shopId)
basket <- findBasket(user, shop)
promo <- findPromotions(shop)
}
}
}



override def


def
case
basketKeeper !
}



def
case
}



def
case
}

}
class PriceCalculator(products: Products, promotions: Promotions) extends Actor {

override def receive: Receive = {

case Calculate => sender ! promotions.price(products)

}

}

Thank you
"com.github.wilaszekg" %% "scala-dynamic-di" % "0.0.1"
https://github.com/wilaszekg/Scala-Dynamic-DI

Weitere ähnliche Inhalte

Ähnlich wie Typesafe dependency injection in reactive applications

Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
 
APIs for catalogs
APIs for catalogsAPIs for catalogs
APIs for catalogsX.commerce
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介Jace Ju
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsŁukasz Chruściel
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactJonne Kats
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails Mohit Jain
 
Making Magento flying like a rocket! (A set of valuable tips for developers)
Making Magento flying like a rocket! (A set of valuable tips for developers)Making Magento flying like a rocket! (A set of valuable tips for developers)
Making Magento flying like a rocket! (A set of valuable tips for developers)Ivan Chepurnyi
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)tompunk
 
Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)GOG.com dev team
 
WordPress Plugin development
WordPress Plugin developmentWordPress Plugin development
WordPress Plugin developmentMostafa Soufi
 
Akka persistence webinar
Akka persistence webinarAkka persistence webinar
Akka persistence webinarpatriknw
 
Building Commerce into Mobile Apps with Shopify's Android Buy SDK
Building Commerce into Mobile Apps with Shopify's Android Buy SDKBuilding Commerce into Mobile Apps with Shopify's Android Buy SDK
Building Commerce into Mobile Apps with Shopify's Android Buy SDKJosh Brown
 
WordPress Theme Workshop: Sidebars
WordPress Theme Workshop: SidebarsWordPress Theme Workshop: Sidebars
WordPress Theme Workshop: SidebarsDavid Bisset
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneRafael Felix da Silva
 
Customizing Your WooCommerce Store
Customizing Your WooCommerce StoreCustomizing Your WooCommerce Store
Customizing Your WooCommerce StoreBarry Kooij
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wildJoe Morgan
 
React Native - Workshop
React Native - WorkshopReact Native - Workshop
React Native - WorkshopFellipe Chagas
 
Custom Post Types and Meta Fields
Custom Post Types and Meta FieldsCustom Post Types and Meta Fields
Custom Post Types and Meta FieldsLiton Arefin
 
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Atwix
 

Ähnlich wie Typesafe dependency injection in reactive applications (20)

Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
APIs for catalogs
APIs for catalogsAPIs for catalogs
APIs for catalogs
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 
RESTful services Design Lab
RESTful services Design LabRESTful services Design Lab
RESTful services Design Lab
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and React
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails
 
Making Magento flying like a rocket! (A set of valuable tips for developers)
Making Magento flying like a rocket! (A set of valuable tips for developers)Making Magento flying like a rocket! (A set of valuable tips for developers)
Making Magento flying like a rocket! (A set of valuable tips for developers)
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
 
Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)
 
WordPress Plugin development
WordPress Plugin developmentWordPress Plugin development
WordPress Plugin development
 
Akka persistence webinar
Akka persistence webinarAkka persistence webinar
Akka persistence webinar
 
Building Commerce into Mobile Apps with Shopify's Android Buy SDK
Building Commerce into Mobile Apps with Shopify's Android Buy SDKBuilding Commerce into Mobile Apps with Shopify's Android Buy SDK
Building Commerce into Mobile Apps with Shopify's Android Buy SDK
 
WordPress Theme Workshop: Sidebars
WordPress Theme Workshop: SidebarsWordPress Theme Workshop: Sidebars
WordPress Theme Workshop: Sidebars
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
Customizing Your WooCommerce Store
Customizing Your WooCommerce StoreCustomizing Your WooCommerce Store
Customizing Your WooCommerce Store
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wild
 
React Native - Workshop
React Native - WorkshopReact Native - Workshop
React Native - Workshop
 
Custom Post Types and Meta Fields
Custom Post Types and Meta FieldsCustom Post Types and Meta Fields
Custom Post Types and Meta Fields
 
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
Сергей Иващенко - Meet Magento Ukraine - Цены в Magento 2
 

Kürzlich hochgeladen

The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech studentsHimanshiGarg82
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Hararemasabamasaba
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benonimasabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...chiefasafspells
 

Kürzlich hochgeladen (20)

The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 

Typesafe dependency injection in reactive applications

  • 1. Typesafe dependency injection in reactive applications Grzegorz Wilaszek ScaLAB, 25.06.2016
  • 3. Typesafe DI in Akka • Problems with asynchronous dependencies • Need for special DI technique • Refactoring with type level programming
  • 6. Introducing an Actor User ShopId Future Shop Future Future Basket PromotionsFuture Basket Actor ask response Products (User,  Shop)  =>  Future[Basket] (ShopId)  =>  Future[Shop] (Shop)  =>  Future[Promotions]
  • 8. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 9. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 10. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 11. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 12. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 13. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 14. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 15. class ConfigurablePriceCalculator(user: User, shopId: ShopId,
 findShop: ShopId => Future[Shop],
 findBasket: (User, Shop) => Future[Basket],
 findPromotions: Shop => Future[Promotions],
 basketKeeper: ActorRef) extends Actor {
 
 private var promotions: Promotions = _
 
 override def preStart(): Unit = {
 super.preStart()
 for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promo <- findPromotions(shop)
 } yield {
 promotions = promo
 self ! basket
 }
 }
 
 override def receive: Receive = waitForBasket
 
 def waitForBasket: Receive = {
 case basket: Basket =>
 basketKeeper ! AskForProducts(basket)
 context become waitForProducts
 }
 
 def waitForProducts: Receive = {
 case products: Products => context become work(products)
 }
 
 def work(products: Products): Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
  • 16. class PriceCalculator(products: Products, promotions: Promotions) extends Actor {
 override def receive: Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }
 Desired actor
  • 17. Refactoring out dependencies for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promotions <- findPromotions(shop)
 products <- (basketKeeper ? AskForProducts(basket)) .mapTo[Products] 
 } yield PriceCalculator.props(products, promotions)

  • 18. Introducing proxy to create actor immediately ProxyProps(for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promotions <- findPromotions(shop)
 products <- (basketKeeper ? AskForProducts(basket)) .mapTo[Products]
 } yield PriceCalculator.props(products, promotions))

  • 19. Introducing proxy to create actor immediately ProxyProps(for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promotions <- findPromotions(shop)
 products <- (basketKeeper ? AskForProducts(basket)) .mapTo[Products]
 } yield PriceCalculator.props(products, promotions))
 monadic futures
  • 20. Introducing proxy to create actor immediately ProxyProps(for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promotions <- findPromotions(shop)
 products <- (basketKeeper ? AskForProducts(basket)) .mapTo[Products]
 } yield PriceCalculator.props(products, promotions))

  • 21. Let’s imagine better API ProxyProps(for {
 shop <- findShop(shopId)
 basket <- findBasket(user, shop)
 promotions <- findPromotions(shop)
 products <- (basketKeeper ? AskForProducts(basket)) .mapTo[Products]
 } yield PriceCalculator.props(products, promotions))

  • 22. Let’s imagine better API ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(findShop)
 .requires(findBasket)
 .requires(findPromotions)
 .requires(basketKeeper, AskForProducts(_), classOf[Products]))
 

  • 23. Let’s imagine better API ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(FutureDependency(findShop))
 .requires(FutureDependency(findBasket))
 .requires(FutureDependency(findPromotions))
 .requires(ActorDependency(basketKeeper, AskForProducts(_), classOf[Products])))
 

  • 24. Let’s imagine better API 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 

  • 25. What should we expect? • Checking availability of dependencies in compile time • Efficient composition of futures
  • 26. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 

  • 27. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 ()
  • 28. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 () (ShopId)
  • 29. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 () (ShopId, User)
  • 30. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 () (ShopId, User) (ShopId, User, Shop)
  • 31. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 () (ShopId, User) (ShopId, User, Shop) (ShopId, User, Shop, Basket) (ShopId, User, Shop, Basket, Promotions) (ShopId, User, Shop, Basket, Promotions, Products)
  • 32. Introducing Shapeless - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 
 
 
 
 
 
 

  • 33. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 
 
 
 
 

  • 34. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 
 
 
 
 

  • 35. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 val text: String = hList.tail.head
 
 
 
 

  • 36. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 val text: String = hList.tail.head
 
 
 
 

  • 37. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 val text: String = hList.tail.head
 
 val numList: List[Int] = hList.tail.tail.head
 
 

  • 38. The basic concept - heterogenous lists 
 val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 
 val num: Int = hList.head
 
 val text: String = hList.tail.head
 
 val numList: List[Int] = hList.tail.tail.head
 
 hList.tail.tail.tail.head

  • 39. Recursive type definition sealed trait HList extends Product with Serializable
 
 final case class ::[+H, +T <: HList](head : H, tail : T) extends HList {
 override def toString = head+" :: "+tail.toString
 }
 
 sealed trait HNil extends HList {
 def ::[H](h : H) = shapeless.::(h, this)
 override def toString = "HNil"
 }
 
 case object HNil extends HNil
  • 40. 
 val hList: ::[Int, ::[String, ::[List[Int], HNil]]] = 1 :: "text" :: List(1, 2, 3) :: HNil
 val hList: Int :: String :: List[Int] :: HNil = 1 :: "text" :: List(1, 2, 3) :: HNil

  • 41. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil]
  • 42. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil] ProxyProps[Promotions :: Products :: HNil]
  • 43. Defining types 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props: Props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId).given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency))
 
 Dependencies[ShopId :: User :: Shop :: Basket :: Promotions :: Products :: HNil] ProxyProps[Promotions :: Products :: HNil]
  • 44. Defining types 
 val shopDependency: FutureDependency[Tuple1[ShopId], Shop] = FutureDependency(findShop) val basketDependency: FutureDependency[(User, Shop), Basket] = FutureDependency(findBasket) val promotionsDependency: FutureDependency[Tuple1[Shop], Promotions] = FutureDependency(findPromotions) val productsDependency: ActorDependency[Tuple1[Basket], Products] = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) 
 

  • 45. Defining types 
 val shopDependency: FutureDependency[Tuple1[ShopId], Shop] = FutureDependency(findShop) val basketDependency: FutureDependency[(User, Shop), Basket] = FutureDependency(findBasket) val promotionsDependency: FutureDependency[Tuple1[Shop], Promotions] = FutureDependency(findPromotions) val productsDependency: FutureDependency[Tuple1[Basket], Products] = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) 
 

  • 51. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[ShopId], Shop] Shapeless allows to use tuple (ShopId) as ShopId :: HNil
  • 52. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[ShopId], Shop] Shapeless allows to use tuple (ShopId) as ShopId :: HNil Shop mustn't be present in dependencies
  • 53. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[ShopId], Shop] Shapeless allows to use tuple (ShopId) as ShopId :: HNil Shop mustn't be present in dependencies Future[ShopId] must be found in dependencies
  • 54. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[ShopId], Shop] Shapeless allows to use tuple (ShopId) as ShopId :: HNil Shop mustn't be present in dependencies Future[ShopId] must be found in dependencies
  • 56. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) .requires(basketDependency) Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[(User, Shop), Basket] Future[User] :: Future[Shop] :: HNil
  • 57. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) .requires(basketDependency) Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[(User, Shop), Basket] Future[User] :: Future[Shop] :: HNil => Future[User :: Shop :: HNil]
  • 58. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) .requires(basketDependency) Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[(User, Shop), Basket]
  • 61. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) .requires(basketDependency) .requires(promotionsDependency) Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[Shop], Promotions]
  • 63. Building computation of Dependencies Dependencies().given(user).given(shopId) .requires(shopDependency) .requires(basketDependency) .requires(promotionsDependency) .requires(productsDependency) Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil FutureDependency[Tuple1[Basket], Products]
  • 64. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
  • 65. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps = ProxyProps(PriceCalculator.props)
  • 66. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps: ProxyProps[Promotions :: Products :: HNil] = ProxyProps(PriceCalculator.props)
  • 67. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps: ProxyProps[Promotions :: Products :: HNil] = ProxyProps(PriceCalculator.props) proxyProps(dependencies)
  • 68. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps: ProxyProps[Promotions :: Products :: HNil] = ProxyProps(PriceCalculator.props) proxyProps(dependencies)
  • 69. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps: ProxyProps[Promotions :: Products :: HNil] = ProxyProps(PriceCalculator.props) val props: Props = proxyProps(dependencies)
  • 70. Starting proxy actor with Dependencies val dependencies: Dependencies[Future[Products] :: Future[Promotions] :: Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil] val proxyProps: ProxyProps[Promotions :: Products :: HNil] = ProxyProps(PriceCalculator.props) val props: Props = proxyProps(dependencies) context.actorOf(props)
  • 71. Proxy behaviour • runs all dependencies as one Future • creates target actor once Future completes • can retry running dependencies Future • can re-run dependencies once target actor terminates
  • 72. Benefits of using Shapeless • Clear and concise API • Reusable dependencies definitions • Efficient asynchronous computation - composing futures • Compiler ensures presence of all required dependencies
  • 74. Recursive implicit lookup trait NotIn[T, L <: HList]
 
 object NotIn {
 implicit def notInHNil[T] = new NotIn[T, HNil] {}
 
 implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H], hIsNotT: <:!<[H, T], notInTail: NotIn[T, L]) =
 new NotIn[T, H :: L] {}
 }
  • 75. Recursive implicit lookup trait NotIn[T, L <: HList]
 
 object NotIn {
 implicit def notInHNil[T] = new NotIn[T, HNil] {}
 
 implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H], hIsNotT: <:!<[H, T], notInTail: NotIn[T, L]) =
 new NotIn[T, H :: L] {}
 }
  • 76. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in
  • 77. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true)
 addUnique(1 :: "txt" :: HNil, 2)

  • 78. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) 

  • 79. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] 
 ?
  • 80. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] 
 ?
  • 81. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] 
 ? ?
  • 82. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] 
 ? ?
  • 83. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] implicit NotIn[Boolean, HNil] 
 ? ? ?
  • 84. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] implicit NotIn[Boolean, HNil] 
 ? ?
  • 85. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] implicit NotIn[Boolean, HNil] 
 ?
  • 86. Usage of recursive implicit def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) = 
 t :: in addUnique(1 :: "txt" :: HNil, true) implicit NotIn[Boolean, Int :: String :: HNil] implicit NotIn[Boolean, String :: HNil] implicit NotIn[Boolean, HNil] 

  • 87. Implicits usage in Shapeless • Stating facts about types • Often recursive: • stop condition • general condition • Compiler finds implicit recursively • Value resolved implicitly may have some functions - the functions can do something useful for given types
  • 88. More useful operations on HList val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) = hList.removeAll[String :: Int :: HNil]

  • 89. More useful operations on HList val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) = hList.removeAll[String :: Int :: HNil]

  • 90. More useful operations on HList val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) = hList.removeAll[String :: Int :: HNil]

  • 91. More useful operations on HList val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) = hList.removeAll[String :: Int :: HNil]
 def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL]) : removeAll.Out = removeAll(l)

  • 92. More useful operations on HList val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
 val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) = hList.removeAll[String :: Int :: HNil]
 def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL]) : removeAll.Out = removeAll(l)
 trait RemoveAll[L <: HList, SL <: HList] { type Out
 def apply(t: L): Out }
  • 93. Recap 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId) .given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency)) context.actorOf(props)
 

  • 94. Declarative and functional asynchronous dependencies
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId) .given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency)) context.actorOf(props)
 

  • 95. Type driven composition 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId) .given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency)) context.actorOf(props)
 

  • 96. Seamless futures composition
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId) .given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency)) context.actorOf(props)
 

  • 97. Compile time checking 
 val shopDependency = FutureDependency(findShop) val basketDependency = FutureDependency(findBasket) val promotionsDependency = FutureDependency(findPromotions) val productsDependency = ActorDependency(basketKeeper, AskForProducts(_), classOf[Products]) val props = ProxyProps(PriceCalculator.props)(Dependencies() .given(shopId) .given(user)
 .requires(shopDependency)
 .requires(basketDependency)
 .requires(promotionsDependency)
 .requires(productsDependency)) context.actorOf(props)
 

  • 98. Less boilerplate class findShop: ShopId => Future[Shop], findBasket: (User, Shop) => Future[Basket], findPromotions: Shop => Future[Promotions], basketKeeper: ActorRef) 
 private var 
 override def super for shop <- findShop(shopId) basket <- findBasket(user, shop) promo <- findPromotions(shop) } } }
 
 override def 
 def case basketKeeper ! }
 
 def case }
 
 def case }
 } class PriceCalculator(products: Products, promotions: Promotions) extends Actor {
 override def receive: Receive = {
 case Calculate => sender ! promotions.price(products)
 }
 }

  • 99. Thank you "com.github.wilaszekg" %% "scala-dynamic-di" % "0.0.1" https://github.com/wilaszekg/Scala-Dynamic-DI