Dependency Injection Beyond the Cake Pattern
Debasish Ghosh @debasishg on twitter Code @  http://github.com/debasishg   Blog @  Ruminations of a Programmer http:// deb...
Open Source Footprints <ul><li>Committer in Akka ( http:// akka.io )  </li></ul><ul><li>Owner/Founder of : </li></ul><ul><...
import  TradeModel._ // a Repository abstraction trait   TradeRepository  { def  fetch(refNo:  String ):  Trade def  updat...
// a Trading service trait   TradeService  { // fetches a trade based on the reference no val  fetchTrade:  TradeRepositor...
// a Trading service trait TradeService { // fetches a trade based on the reference no val fetchTrade:  TradeRepository  =...
suppose we would like to use a  Redis based Repository  .. class   RedisTradeRepository   extends   TradeRepository  { def...
define  partial application  of the service methods  using the Redis based repository implementation in a  separate module...
define  partial application  of the service methods  using the Redis based repository implementation in a  separate module...
val  fetchTrade:  TradeRepository  =>  String  =>  Trade val  fetchTrade_c:  String  =>  Trade import  TradeServiceWithRed...
instead of currying individual functions, we can curry a  composed function  ..  // warning: needs scalaz! val  withTrade ...
domain  rule .. <ul><li>enrichment of trade is done in steps: </li></ul><ul><li>get the tax/fee ids for a trade </li></ul>...
// enrichment of trade // implementation follows problem domain model val  enrich =  for  { // get the tax/fee ids for a t...
// get the list of tax/fees for this trade val  forTrade:  Trade  =>  Option [ List [ TaxFeeId ]] = {trade =>  // .. imple...
val  enrich =  for  { // get the tax/fee ids for a trade taxFeeIds  <- forTrade  // calculate tax fee values taxFeeValues ...
The Reader Monad <ul><li>Note how the Trade is being delayed in injection </li></ul><ul><li>How we don’t have to repeat th...
trait   TradeService  { def  fetchTrade(refNo:  String )( implicit  repo:  TradeRepository ) = repo.fetch(refNo) def  upda...
implicit   object   RedisTradeRepository   extends   TradeRepository  { def  fetch(refNo:  String ):  Trade  = //.. Redis ...
import  TradeService ._ import  Repositories.RedisTradeRepository def  run = { updateTrade(fetchTrade(&quot;r-123&quot;)) ...
 
Próximos SlideShares
Carregando em…5
×

Dependency Injection in Scala - Beyond the Cake Pattern

14.220 visualizações

Publicada em

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

Publicada em: Tecnologia
  • At slide n17 we have an 'implicit repo' at each method, why? Isnt that possible to leave only one implicit right inside TradeService trait body?

    Cheers!
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui

Dependency Injection in Scala - Beyond the Cake Pattern

  1. 1. Dependency Injection Beyond the Cake Pattern
  2. 2. Debasish Ghosh @debasishg on twitter Code @ http://github.com/debasishg Blog @ Ruminations of a Programmer http:// debasishg.blogspot.com
  3. 3. Open Source Footprints <ul><li>Committer in Akka ( http:// akka.io ) </li></ul><ul><li>Owner/Founder of : </li></ul><ul><ul><li>sjson (JSON serialization library for Scala objects – http://github.com/debasishg/sjson ) </li></ul></ul><ul><ul><li>scala-redis (Redis client for Scala – http://github.com/debasishg/scala-redis ) </li></ul></ul><ul><ul><li>scouchdb (Scala driver for Couchdb – http:// github.com/debasishg/scouchdb ) </li></ul></ul>
  4. 4. import TradeModel._ // a Repository abstraction trait TradeRepository { def fetch(refNo: String ): Trade def update(trade: Trade ): Trade }
  5. 5. // a Trading service trait 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)} }
  6. 6. // a Trading service trait 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
  7. 7. suppose we would like to use a Redis based Repository .. 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 ..
  8. 8. 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 ) }
  9. 9. 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
  10. 10. val fetchTrade: TradeRepository => String => Trade val fetchTrade_c: String => Trade import  TradeServiceWithRedisRepo._ val  t = fetchTrade_c(&quot;ref-123&quot;) by using the appropriate module, we can switch Repository implementations ..
  11. 11. 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 binding Of functions
  12. 12. domain rule .. <ul><li>enrichment of trade is done in steps: </li></ul><ul><li>get the tax/fee ids for a trade </li></ul><ul><li>calculate tax/fee values </li></ul><ul><li>enrich trade with net cash amount </li></ul>
  13. 13. // enrichment of trade // implementation follows problem domain model 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)
  14. 14. // get the list of tax/fees for this trade val forTrade: Trade => Option [ List [ TaxFeeId ]] = {trade => // .. implementation } // all tax/fees for a specific trade val taxFeeCalculate: Trade => List [ TaxFeeId ] => List [( TaxFeeId , BigDecimal )] = {t => tids => //.. implementation } val enrichTradeWith: Trade => List [( TaxFeeId , BigDecimal )] => BigDecimal = {trade => taxes => //.. implementation }
  15. 15. 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
  16. 16. The Reader Monad <ul><li>Note how the Trade is being delayed in injection </li></ul><ul><li>How we don’t have to repeat the Trade argument in each invocation of the functions </li></ul><ul><li>This is an implementation of the Reader monad – Trade is only being read to compute all calculations </li></ul><ul><li>Partial Application is the key </li></ul>
  17. 17. 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 ..
  18. 18. 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 ..
  19. 19. import TradeService ._ import Repositories.RedisTradeRepository def run = { updateTrade(fetchTrade(&quot;r-123&quot;)) //.. }

×