Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DI in Scala with Cake Pattern

This article explains Dependency Injection via Scala's Cake Pattern.

My understanding of this pattern's benefit is that traits can be mixed in (production v. test) with static checking.

In Mr. Bonér's example, he lists this finished (per example) code:

UserRepositoryComponent and UserServiceComponent

I added comments per my understanding.

trait UserRepositoryComponent {
  val userRepository: UserRepository    // stand-alone component

  class UserRepository {      
    ...                      // actual implementation here
  }
} 
trait UserServiceComponent { 
  this: UserRepositoryComponent =>      //Requires a mixed-in UserRepo*Component

  val userService: UserService  

  class UserService {
    ...                      // actual implementation here
  }
}

My understanding is that the Service depends on injection of a Repository component.

For production purposes, the following can be used to wire a "production" Repository component into the UserServiceComponent:

object ComponentRegistry extends 
  UserServiceComponent with 
  UserRepositoryComponent 
{
  val userRepository = new UserRepository
  val userService = new UserService
}

If our production code wanted to use the userRepository or userService, is the correct way to use them via a simple import?

I think that I understand half of the article up to this point, but I'm not sure how to use the ComponentRegistry object.

like image 484
Kevin Meredith Avatar asked Feb 12 '23 06:02

Kevin Meredith


1 Answers

You're running head first into the bakery of doom bro: What are some compelling use cases for dependent method types?

To answer your question, the proper way to use userService would be to use another trait and cake it up:

trait Example { this: UserServiceComponent => 
   def getExampleUser() = userService.getUser("ExampleUser")
}

Now whatever this new trait does isn't directly coupled to anything like the object ComponentRegistry. Instead your application becomes this:

object Application extends 
  Example with
  UserServiceComponent with 
  UserRepositoryComponent 
{
  val userRepository = new UserRepository
  val userService = new UserService
}

Anyway, you should run for the hills because if you really want to use cake you should be doing something more like this:

trait UserRepositoryComponent {

  type UserRepository <: UserRepositoryLike

  val userRepository: UserRepository

  trait UserRepositoryLike {
    def getUserOrSomething()
  }

}

trait UserRepositoryComponentImpl extends UserRepositoryComponent {

  type UserRepository = UserRepositoryImpl
  val userRepository = new UserRepositoryImpl

  class UserRepositoryImpl extends UserRepositoryLike {
    override def getUserOrSomething() = ???
  }

}

trait UserServiceComponent {
  this: UserRepositoryComponent =>

  type UserService <: UserServiceLike
  val userService: UserService

  trait UserServiceLike {
    def getUserNameById(id: Int): String
  }

}

trait UserServiceComponentImpl extends UserServiceComponent {
  this: UserRepositoryComponent =>

  type UserService = UserServiceImpl
  val userService = new UserServiceImpl

  class UserServiceImpl extends UserServiceLike {
    override def getUserNameById(id: Int) = userRepository.getUserOrSomething
  }

}

trait Example {
  this: UserServiceComponent =>

  def getExampleUser() = userService.getUserNameById(1)

}

object Application extends
Example with
UserRepositoryComponentImpl with
UserServiceComponentImpl

Now save yourself some time, drop the cake pattern, and do something simple.

like image 192
Noah Avatar answered Feb 19 '23 06:02

Noah