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 co
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
implementation AlternativeUserService
implementation 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.