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.
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.
Related
I have a code in my Play Scala (2.5x, 2.11.11) app which has been running just fine so far (it is based on the following link: https://fizzylogic.nl/2016/11/27/authorize-access-to-your-play-application-using-action-builders-and-action-functions/). But now I need to pass another class instance to ApplicationAuthorizationHandler class (NOTE: throughout my code I am using Guice DI for injecting parameters into class constructors).
Current code:
class ApplicationAuthorizationHandler
extends AuthorizationHandler {
...
}
trait AuthorizationHandler {
...
}
trait AuthorizationCheck {
def authorizationHandler: AuthorizationHandler = new ApplicationAuthorizationHandler
object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))
authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
}
}
}
//Example controller using this trait AuthorizationCheck
class MyController #Inject() extends Controller with AuthorizationCheck {
def myAction = AuthenticatedAction { implicit request =>
...
}
Desired code:
class ApplicationAuthorizationHandler #Inject() (userService: UserService)
extends AuthorizationHandler {
...
// userService is used here
}
But since the instance of ApplicationAuthorizationHandler is instantiated inside trait AuthorizationCheck I can't inject UserService instance into it. I am Mixin this trait with all controllers so would like to keep the same way unless there is a better way (and there must be).
First, is there a way to inject directly into class/trait method ?
Alternatively, is there a way where I don't instantiate ApplicationAuthorizationHandler in trait AuthorizationCheck and pass it during run-time inside the controller ?
Or any other way ?
A trait does not need to provide an implementation, so you can have something like:
trait AuthorizationHandler {
...
}
class ApplicationAuthorizationHandler extends AuthorizationHandler {
...
}
trait AuthorizationCheck {
// just declaring that implementations needs to provide a
def authorizationHandler: AuthorizationHandler
object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))
authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
}
}
}
// So, now this controller needs to provide a concrete implementation
// of "authorizationHandler" as declared by "AuthorizationCheck".
// You can do it by injecting a "AuthorizationHandler" as a val with
// name authorizationHandler.
class MyController #Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
def myAction = AuthenticatedAction { implicit request =>
...
}
}
And of course, you need to provide a module to bind AuthorizationHandler to ApplicationAuthorizationHandler:
import play.api.inject._
class AuthorizationHandlerModule extends SimpleModule(
bind[AuthorizationHandler].to[ApplicationAuthorizationHandler]
)
Of course, ApplicationAuthorizationHandler can have its own dependencies injected. You can see more details at our docs.
There are many cases when you cannot use the #Inject approach of guice. This is true when dependencies are needed inside of trait and also actors.
The approach I use in these cases is that I put my injector in a object
object Injector {
val injector = Guice.createInjector(new ProjectModule())
}
since the above is inside of an object, you can access it from anywhere. (its like a singleton).
Now inside your trait or an actor when you need the user service do
trait Foo {
lazy val userService = Injector.injector.getInstance(classOf[UserService])
}
Don't forget to make the variable lazy, because you want the instance to be created as late as possible when the injector has already been created.
I have a Scala trait
trait UserRepository {
def findByEmail(email: String): User
}
I would like to inject this into a service with MacWire
class AccountService(){
val userRepo = wire[UserRepository]
}
And then use it in a test or class
class AccountServiceSpec {
val userRepo = new UserRepositoryImpl()
val accountSvc = new AccountService() //<--not manually injecting repo in service constructor
}
but I'm getting a compile error in the service class
Cannot find a public constructor nor a companion object for
accounts.repository.UserRepository
You may try to transform userRepo to class parameter, that allows macwire automatically provide its value for service:
import com.softwaremill.macwire._
case class User(email: String)
trait UserRepository {
def findByEmail(email: String): User
}
class AccountService(val userRepo: UserRepository)
class UserRepositoryImpl extends UserRepository{
def findByEmail(email: String): User = new User(email)
}
class AccountServiceSpec {
val userRepo = new UserRepositoryImpl()
val accountSvc = wire[AccountService] //<--not manually injecting repo in service constructor
}
I'm currently working to use the cake pattern on my application.
On exemples I have found across the web the exemples are kind of basic but doesn't involve more complex needs. What I'd like to do is not so fancy: I would like to have inside a cake pattern application, 2 services of the same type, using different implementations.
trait UserServiceComponent {
self: UserRepositoryComponent =>
val userService: UserService
class DefaultUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
}
class AlternativeUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
}
}
trait UserService extends RepositoryDelegator[User] {
def getPublicProfile(id: String): Either[Error, User]
}
It works fine if I use one implementation of the UserService at a time, but if I need both implementations in the same time, I don't really know how to do it.
Should I create 2 distinct components? Each one exposing a different userService value name? (defaultUserService/alternativeUserService). Using one component for both implementation I don't know how other components would be able to know which implementation is used when using the name userService since there are 2 distinct implementations in my application.
By the way, as the component expresses the dependency to the UserRepositoryComponent, while it is not needed by all implementations, I find it a bit weird to have only one component right?
Imagine I don't want to build the full application which needs both implementations, but I need, for tests, to build only the AlternativeUserService which doesn't need the UserRepositoryComponent, it would be weird to have to provide this dependency as it will not be used.
Can someone give me some advices so that I know what to do?
Kind of related question:
Cake pattern: how to get all objects of type UserService provided by components
Thanks
First things first, you should decouple the UserServiceComponent from the implementations of UserService:
trait UserService extends RepositoryDelegator[User] {
def getPublicProfile(id: String): Either[Error, User]
}
trait UserServiceComponent {
val userService: UserService
}
trait DefaultUserServiceComponent extends UserServiceComponent { self: UserRepositoryComponent =>
protected class DefaultUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
}
val userService: UserService = new DefaultUserService
}
trait AlternativeUserServiceComponent extends UserServiceComponent {
protected class AlternativeUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
}
val userService: UserService = new AlternativeUserService
}
If that looks verbose, well it is. The cake pattern is not particularly concise.
But notice how it solves your problem about having a dependency to UserRepositoryComponent even when not actually required (such as when only using AlternativeUserService).
Now, all we have to do when instantiating the application is to mix either DefaultUserServiceComponent or AlternativeUserServiceComponent.
If you happen to need to access to both implementations, you should indeed expose two userService value names. Well in fact, 3 names, such as:
defaultUserService for the DefaultUserService implementation
alternativeUserService for the AlternativeUserService implementation
mainUserService for any UserService implementation (the application chooses which one at "mix time").
By example:
trait UserService extends RepositoryDelegator[User] {
def getPublicProfile(id: String): Either[Error, User]
}
trait MainUserServiceComponent {
val mainUserService: UserService
}
trait DefaultUserServiceComponent { self: UserRepositoryComponent =>
protected class DefaultUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
}
val defaultUserService: UserService = new DefaultUserService
}
trait AlternativeUserServiceComponent {
protected class AlternativeUserService extends UserService {
def getPublicProfile(id: String): Either[Error, User] = ??? // call webservice here for exemple...
}
val alternativeUserService: UserService = new AlternativeUserService
}
Then you can instantiate your cake like this:
object MyApp
extends MainUserServiceComponent
with DefaultUserServiceComponent
with AlternativeUserServiceComponent
with MyUserRepositoryComponent // Replace with your real UserRepositoryComponent here
{
//val userService = defaultUserService
val mainUserService = alternativeUserService
}
In the above example, services that explicitly want to access the DefaultUserService would put DefaultUserServiceComponent as a dependecy of their component (same for AlternativeUserService and AlternativeUserServiceComponent), and services that just need some UserService would instead put MainUserServiceComponent as a dependency. You decide at "mix time" which service mainUserService points to (here, it points to the DefaultUserService implementation.
I'm trying to use the cake pattern for the first time.
I kind of understand how it works, but would like to know if it is possible to mix already mixed traits or something like that.
What I would like is to build a global application with the cake pattern.
And I want another version of that application which would be the same, except at the repository level.
Is it possible to do something like:
trait application extends DefaultUserServiceComponent with MongoUserRepositoryComponent
object realApplication extends application
object fakeApplication extends FakeUserRepositoryComponent with application
I mean: reusing the already built application, when building a fake application using fake repositories?
Short answer: No. You would be inheriting conflicting members. See the following code snippet:
trait Repository {def authenticate(username: String, password: String): String}
trait UserServiceComponent {self: UserRepositoryComponent =>
val userService: UserService = new UserService
class UserService {
def authenticate(username: String, password: String): String =
repository.authenticate(username, password)
}
}
trait UserRepositoryComponent {
def repository: Repository
}
trait MongoUserRepositoryComponent extends UserRepositoryComponent {
val repository: Repository =
new Repository {def authenticate(username: String, password: String) = "MongoAuthed"}
}
trait MockUserRepositoryComponent extends UserRepositoryComponent {
val repository: Repository =
new Repository {def authenticate(username: String, password: String) = "MockAuthed"}
}
trait Application extends UserServiceComponent with MongoUserRepositoryComponent
object RealApplication extends Application
// The following will be an error: "object FakeApplication inherits conflicting members:"
object FakeApplication extends Application with MockUserRepositoryComponent
Instead, to have the desired behavior, define Application as such:
trait Application extends UserServiceComponent {self: UserRepositoryComponent =>}
object RealApplication extends Application with MongoUserRepositoryComponent
object FakeApplication extends Application with MockUserRepositoryComponent
To keep the hierarchy given in the OP, you would need to modify the code as such:
trait MongoUserRepositoryComponent extends UserRepositoryComponent {
private val _repository = new Repository {def authenticate(username: String, password: String) = "MongoAuthed"}
def repository: Repository = _repository
}
trait MockUserRepositoryComponent extends UserRepositoryComponent {
private val _repository = new Repository {def authenticate(username: String, password: String) = "MockAuthed"}
def repository: Repository = _repository
}
trait Application extends UserServiceComponent with MongoUserRepositoryComponent
object RealApplication extends Application
object FakeApplication extends Application with MockUserRepositoryComponent {
override val repository: Repository = super[MockUserRepositoryComponent].repository
}
The additional private val _repository's are necessary so that we can define repository as a function, so that it can be used as an override in FakeApplication. (Using super[type] to override only works with functions).
EDIT: In the end, the purpose of the cake pattern is to develop a hierarchy where one does not need to override as in the last code snippet I provided. In reality, the trait Application should not exist at all, only RealApplication and FakeApplication. (In the 2nd code snippet I'm essentially doing nothing more than renaming UserServiceComponent to Application).
Most of the examples of the Cake Pattern I've come across appear to consider dependencies as singleton type services; where there is only one instance of each type in the final assembly of components. Is it possible to write a configuration that has more than one instance of a particular type, perhaps configured in different ways, when using the Cake Pattern for dependency injection?
Consider the following components. Generic HTTP service:
trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
val httpService:HttpService
class HttpServiceImpl(address:String) extends HttpService {
def get(query:String):String = ...
}
}
Trade & Company services, that each depend on an HttpService, which may be different instances:
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
this:HttpServiceComponent => // Depends on HttpService
val tradeService:TradeService
class TradeServiceImpl extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
this:HttpServiceComponent => // Depends on different HttpService instance
val companyService:CompanyService
class CompanyServiceImpl extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
Main app component that depends on Trade & Company services:
trait App { def run(exchange:String):Unit }
trait AppComponent {
this:CompanyServiceComponent with TradeServiceComponent =>
val app:App
class AppImpl extends App {
def run(exchange:String) =
companyService.getCompanySymbols(exchange).split(",").foreach(sym => {
val lastTrade = tradeService.lastTrade(sym)
printf("Last trade for %s: %s".format(sym, lastTrade))
})
}
}
Is it possible to wire up the App so that its TradeService uses a HttpService that points to one address, and its CompanySerivce uses a different HttpService instance pointing to another address?
As you can see from the answers (notably Daniel's, but also your own), it is possible, but it doesn't look elegant. The difficulty appears because when you use the Cake pattern, you mix all required traits into one object (using "with" keyword), and you cannot mix a trait more than once into one instance. That is how mixins work, and the Cake is based on them.
The fact you can force Cake to handle non-singleton dependencies doesn't mean you should do it. I would advise you to simply use plain-old constructor in such cases, that is where self-type annotation doesn't fit well:
trait HttpService { ... }
/* HttpServiceImpl has become a top-level class now,
* as the Cake pattern adds no more value here.
* In addition, trait HttpServiceComponent gets deleted */
class HttpServiceImpl(address:String) extends HttpService {
...
}
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
// The dependency on HttpService is no longer declared as self-type
val tradeService:TradeService
// It is declared as a constructor parameter now
class TradeServiceImpl(httpService: HttpService) extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
// Again, self-type annotation deleted
val companyService:CompanyService
// Again, the dependency is declared as a constructor parameter
class CompanyServiceImpl(httpService: HttpService) extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
The App and AppComponent traits stay in their original form. Now you can use the all components in the following way:
object App {
def main(args:Array[String]):Unit = {
val appAssembly = new AppComponent
with TradeServiceComponent
with CompanyServiceComponent {
// Note, that HttpServiceComponent it neither needed nor mixed-in now
val tradeService = new TradeServiceImpl(
new HttpServiceImpl("http://trades-r-us.com"))
val companyService = new CompanyServiceImpl(
new HttpServiceImpl("http://exchange-services.com"))
val app = new AppImpl
}
appAssembly.app.run(args(0))
}
}
Also, you may want do double-check if the Cake pattern is really best suited for your needs, as it is actually a complex pattern and dependency injection is only one part of it. If you use it only for DI, I would advise you to use a simpler solution. I've blogged about that here.
Since each "client" may need a different implementation, you could just parameterize the service.
trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
def httpService(name: String):HttpService
class HttpServiceImpl(address:String) extends HttpService {
def get(query:String):String = ...
}
}
To be used like this:
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
this:HttpServiceComponent => // Depends on HttpService
val tradeService:TradeService
class TradeServiceImpl extends TradeService {
def lastTrade(symbol:String):String =
httpService("TradeService").get("symbol=" + symbol)
}
}
The final mix would then do something like this:
trait AppComponent {
this:CompanyServiceComponent with TradeServiceComponent =>
val httpServices = Map( "TradeService" -> new HttpServiceImpl("http://trades-r-us.com"),
"CompanyService" -> new HttpServiceImpl("http://exchange-services.com"))
def httpService(name: String) = httpServices(name)
This compiles and runs as expected, but it leaves a lot to be desired:
object App {
def main(args:Array[String]):Unit = {
val tradeServiceAssembly = new TradeServiceComponent with HttpServiceComponent {
val httpService = new HttpServiceImpl("http://trades-r-us.com")
val tradeService = new TradeServiceImpl
}
val companyServiceAssembly = new CompanyServiceComponent with HttpServiceComponent {
val httpService = new HttpServiceImpl("http://exchange-services.com")
val companyService = new CompanyServiceImpl
}
val appAssembly = new AppComponent
with TradeServiceComponent
with CompanyServiceComponent
with HttpServiceComponent {
lazy val httpService = error("Required for compilation but not used")
val tradeService = tradeServiceAssembly.tradeService
val companyService = companyServiceAssembly.companyService
val app = new AppImpl
}
appAssembly.app.run(args(0))
}
}