Dependency Injection in Scala - Beyond the Cake Pattern

20

Click here to load reader

description

Discusses how to do dependency injection in Scala using functional programming.

Transcript of Dependency Injection in Scala - Beyond the Cake Pattern

Page 1: Dependency Injection in Scala - Beyond the Cake Pattern

Dependency Injection

Beyond the Cake Pattern

Page 2: Dependency Injection in Scala - Beyond the Cake Pattern

Debasish Ghosh

@debasishg on twitter

Code @ http://github.com/debasishg

Blog @ Ruminations of a Programmerhttp://debasishg.blogspot.com

Page 3: Dependency Injection in Scala - Beyond the Cake Pattern

Open Source Footprints

• Committer in Akka (http://akka.io)

• Owner/Founder of :– sjson (JSON serialization library for Scala

objects – http://github.com/debasishg/sjson)

– scala-redis (Redis client for Scala – http://github.com/debasishg/scala-redis)

– scouchdb (Scala driver for Couchdb – http://github.com/debasishg/scouchdb)

Page 4: Dependency Injection in Scala - Beyond the Cake Pattern

import TradeModel._

// a Repository abstractiontrait TradeRepository { def fetch(refNo: String): Trade def update(trade: Trade): Trade}

Page 5: Dependency Injection in Scala - Beyond the Cake Pattern

// a Trading servicetrait TradeService {

// fetches a trade based on the reference no val fetchTrade: TradeRepository => String => Trade = {repo => refNo => repo.fetch(refNo)}

// updates a trade with the given values val updateTrade: TradeRepository => Trade => Trade = {repo => trade => repo.update(trade)}

}

Page 6: Dependency Injection in Scala - Beyond the Cake Pattern

// a Trading servicetrait TradeService {

// fetches a trade based on the reference no val fetchTrade: TradeRepository => String => Trade = {repo => refNo => repo.fetch(refNo)}

// updates a trade with the given values val updateTrade: TradeRepository => Trade => Trade = {repo => trade => repo.update(trade)}

} Repository is still abstract

Page 7: Dependency Injection in Scala - Beyond the Cake Pattern

suppose we would like to use a Redis basedRepository ..

class RedisTradeRepository extends TradeRepository { def fetch(refNo: String): Trade = //.. Redis based implementation

def update(trade: Trade): Trade = //.. Redis based implementation}

need to indicate that to the service class ..

Page 8: Dependency Injection in Scala - Beyond the Cake Pattern

define partial application of the service methods using the Redis based repository implementation in a separate module ..

object TradeServiceWithRedisRepo extends TradeService {

// partially applied functions val fetchTrade_c = fetchTrade(new RedisTradeRepository)

val updateTrade_c = updateTrade(new RedisTradeRepository)}

Page 9: Dependency Injection in Scala - Beyond the Cake Pattern

define partial application of the service methods using the Redis based repository implementation in a separate module ..

object TradeServiceWithRedisRepo extends TradeService {

// partially applied functions val fetchTrade_c = fetchTrade(new RedisTradeRepository)

val updateTrade_c = updateTrade(new RedisTradeRepository)}

Concrete implementation injected

Page 10: Dependency Injection in Scala - Beyond the Cake Pattern

val fetchTrade: TradeRepository => String => Trade

val fetchTrade_c: String => Trade

import TradeServiceWithRedisRepo._val t = fetchTrade_c("ref-123")

by using the appropriate module, we can switch Repositoryimplementations ..

Page 11: Dependency Injection in Scala - Beyond the Cake Pattern

instead of currying individual functions, we can curry

a composed function ..

// warning: needs scalaz!val withTrade = for { t <- fetchTrade n <- updateTrade}yield(t map n)

val withTrade_c = withTrade(new RedisTradeRepository)

Monadic bindingOf functions

Page 12: Dependency Injection in Scala - Beyond the Cake Pattern

domain rule ..

enrichment of trade is done in steps:

1. get the tax/fee ids for a trade2. calculate tax/fee values3. enrich trade with net cash amount

Page 13: Dependency Injection in Scala - Beyond the Cake Pattern

// enrichment of trade// implementation follows problem domain modelval enrich = for { // get the tax/fee ids for a trade taxFeeIds <- forTrade

// calculate tax fee values taxFeeValues <- taxFeeCalculate

// enrich trade with net amount netAmount <- enrichTradeWith}yield((taxFeeIds map taxFeeValues) map netAmount)

Page 14: Dependency Injection in Scala - Beyond the Cake Pattern

// get the list of tax/fees for this tradeval forTrade: Trade => Option[List[TaxFeeId]] ={trade => // .. implementation}

// all tax/fees for a specific tradeval taxFeeCalculate: Trade => List[TaxFeeId] => List[(TaxFeeId, BigDecimal)] = {t => tids => //.. implementation}

val enrichTradeWith: Trade => List[(TaxFeeId, BigDecimal)] => BigDecimal = {trade => taxes => //.. implementation}

Page 15: Dependency Injection in Scala - Beyond the Cake Pattern

val enrich = for { // get the tax/fee ids for a trade taxFeeIds <- forTrade

// calculate tax fee values taxFeeValues <- taxFeeCalculate

// enrich trade with net amount netAmount <- enrichTradeWith}yield((taxFeeIds map taxFeeValues) map netAmount)

(TradeModel.Trade) => Option[BigDecimal]

:type enrich

Page 16: Dependency Injection in Scala - Beyond the Cake Pattern

The Reader Monad

• Note how the Trade is being delayed in injection

• How we don’t have to repeat the Trade argument in each invocation of the functions

• This is an implementation of the Reader monad – Trade is only being read to compute all calculations

• Partial Application is the key

Page 17: Dependency Injection in Scala - Beyond the Cake Pattern

trait TradeService { def fetchTrade(refNo: String)(implicit repo: TradeRepository) = repo.fetch(refNo)

def updateTrade(trade: Trade)(implicit repo: TradeRepository) = repo.update(trade)}

object TradeService extends TradeService

typeclass based dependency injection ..

Page 18: Dependency Injection in Scala - Beyond the Cake Pattern

implicit object RedisTradeRepository extends TradeRepository {

def fetch(refNo: String): Trade = //.. Redis based implementation

def update(trade: Trade): Trade = //.. Redis based implementation}

typeclass instance for Redis based repository ..

Page 19: Dependency Injection in Scala - Beyond the Cake Pattern

import TradeService._import Repositories.RedisTradeRepository

def run = { updateTrade(fetchTrade("r-123")) //..}

Page 20: Dependency Injection in Scala - Beyond the Cake Pattern