SlideShare a Scribd company logo
1 of 99
Download to read offline
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

More Related Content

Similar to Typesafe dependency injection in reactive applications

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
 
Akka persistence webinar
Akka persistence webinarAkka persistence webinar
Akka persistence webinar
patriknw
 

Similar to 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
 

Recently uploaded

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 

Recently uploaded (20)

KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
SQL Injection Introduction and Prevention
SQL Injection Introduction and PreventionSQL Injection Introduction and Prevention
SQL Injection Introduction and Prevention
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdfMicrosoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityAPVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
 
how-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdfhow-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdf
 
How to pick right visual testing tool.pdf
How to pick right visual testing tool.pdfHow to pick right visual testing tool.pdf
How to pick right visual testing tool.pdf
 
5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand
 

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