How to separate business classes and auxiliary traits in Cake Pattern? - scala

The Cake Pattern looks as follows:
trait UserRepositoryComponent {
val userRepository: UserRepository
class UserRepository {...}
}
trait UserServiceComponent {this: UserRepositoryComponent =>
val userService: UserService
class UserService {...}
}
The classes UserService and UserRepository are real business classes. We can use them with and without the Cake pattern.
The traits UserServiceComponent and UserRepositoryComponent, on the contrary, are just auxiliary boilerplate code rather than real business components. We need them just to implement the pattern.
Given that, I would not like UserService and UserRepository to be inner classes of UserServiceComponent and UserRepositoryComponent. Does it make sense? Can I implement them not as inner classes?

You don’t need to wrap your implementation classes, e.g. you could write something like that:
trait UserRepository { ... }
trait UserService { this: UserRepository => ... }
But creating you components would need to instantiate a single object containing all your components at once:
new UserRepositoryImpl with UserServiceImpl
So, methods from UserRepository and UserService would be mixed in a single instance, which is not necessarily what you want (and which would cause problems if your components define methods with the same name).
Thus, wrapping your components in “boilerplate” traits creates useful namespaces.

Related

What is the difference between mixins and inheritance?

I am trying to understand the Mixins in the context of scala. In particular I wanted to know difference between concepts of inheritance and Mixins.
The definition of Mixin in wiki says :
A mixin class acts as the parent class, containing the desired functionality. A subclass can then inherit or simply reuse this functionality, but not as a means of specialization. Typically, the mixin will export the desired functionality to a child class, without creating a rigid, single "is a" relationship. Here lies the important difference between the concepts of mixins and inheritance, in that the child class can still inherit all the features of the parent class, but, the semantics about the child "being a kind of" the parent need not be necessarily applied.
In the above definition, I am not able to understand the statements marked in bold. what does it mean that
A subclass can inherit functionality in mixin but not as a means of specialization
In mixins, the child inherits all features of parent class but semantics about the child "being a kind" the parent need not be necessarily applied. - How can a child extend a parent and not necessarily a kind of Parent ? Is there an example like that.
I'm not sure I understood your question properly, but if I did, you're asking how something can inherit without really meaning the same thing as inheriting.
Mixins, however, aren't inheritance – it's actually more similar to dynamically adding a set of methods into an object. Whereas inheritance says "This thing is a kind of another thing", mixins say, "This object has some traits of this other thing." You can see this in the keyword used to declare mixins: trait.
To blatantly steal an example from the Scala homepage:
abstract class Spacecraft {
def engage(): Unit
}
trait CommandoBridge extends Spacecraft {
def engage(): Unit = {
for (_ <- 1 to 3)
speedUp()
}
def speedUp(): Unit
}
trait PulseEngine extends Spacecraft {
val maxPulse: Int
var currentPulse: Int = 0
def speedUp(): Unit = {
if (currentPulse < maxPulse)
currentPulse += 1
}
}
class StarCruiser extends Spacecraft
with CommandoBridge
with PulseEngine {
val maxPulse = 200
}
In this case, the StarCruiser isn't a CommandoBridge or PulseEngine; it has them, though, and gains the methods defined in those traits. It is a Spacecraft, as you can see because it inherits from that class.
It's worth mentioning that when a trait extends a class, if you want to make something with that trait, it has to extend that class. For example, if I had a class Dog, I couldn't have a Dog with PulseEngine unless Dog extended Spacecraft. In that way, it's not quite like adding methods; however, it's still similar.
A trait (which is called mixin when mixed with a class) is like an interface in Java (though there are many differences) where you can add additional features to a class without necessarily having "is a" relationship. Or you can say that generally traits bundle up features which can be used by multiple independent classes.
To give you an example from Scala library, Ordered[A] is a trait which provides implementation for some basic comparison operations (like <, <=, >, >=) to classes that can have data with natural ordering.
For example, let's say you have your own class Number and subclasses EvenNumber and OddNumber as shown below.
class Number(val num : Int) extends Ordered[Number] {
override def compare(that : Number) = this.num - that.num
}
trait Half extends Number {
def half() = num / 2
}
trait Increment extends Number {
def increment() = num + 1
}
class EvenNumber(val evenNum : Int) extends Number(evenNum) with Half
class OddNumber(val oddNum : Int) extends Number(oddNum) with Increment
In the example above, classes EvenNumber and OddNumber share is a relationship with Number but EvenNumber does not have "is a" relation with Half neither OddNumber share "is a" relation with Increment.
Another important point is even though class Number uses extends Ordered syntax, it means that Number has an implicit is a relationship with superclass of Ordered ie Any.
I think its very usage dependent. Scala being a multi-paradigm language makes it powerful as well as a bit confusing at times.
I think Mixins are very powerful when used the right way.
Mixins should be used to introduce behavior and reduce bolierplate.
A trait in Scala can have implementations and it is tempting to extend them and use them.
Traits could be used for inheritance. It can also be called mixins however that in my opinion is not the best way to use mixin behavior. In this case you could think of traits as Java Abstract Classes. Wherein you get subclasses that are "type of" the super class (the trait).
However Traits could be used as proper mixins as well. Now using a trait as a mixin depends on the implementation that is "how you mix it in". Mostly its a simple question to ask yourself . It is "Is the subclass of the trait truly a kind of the trait or are the behaviors in the trait behaviors that reduce boilerplate".
Typically it is best implemented by mixing in traits to objects rather than extending the trait to create new classes.
For example consider the following example:
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are records"}
override def updateRecords(records:String){
println("Updated "+records)
}
}
//One concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"Records returned from DAO2"}
override def updateRecords(records:String){
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
concreteDAO is a trait which extends AbstractDAO - This is inheritance
val wiredObject = new service with concreteDAO -
This is proper mixin behavior
Since the service trait mandates the mixin of a AbstractDAO. It would be just wrong for Service to extend ConcreteDAO anyways because the service required AbstractDAO it is not a type of AbstractDAO.
Instead you create instances of service with different mixins.
The difference between mixin and inheritance is at semantic level. At syntax level they all are the same.
To mix in a trait, or to inherit from a trait, they all use extends or with which is the same syntax.
At semantic level, a trait that is intended to be mixed in usually doesn't have a is a relationship with the class mixining it which differs to a trait that is intended to be inherited.
To me, whether a trait is a mixin or parent is very subjective, which often time is a source of confusion.
I think it is talking about the actual class hierarchy. For example, a Dog is a type of Animal if it extends from the class (inheritance). It can be used wherever an Animal parameter is applicable.

Dependency injection with abstract class and object in Play Framework 2.5

I'm trying to migrate from Play 2.4 to 2.5 avoiding deprecated stuff.
I had an abstract class Microservice from which I created some objects. Some functions of the Microservice class used play.api.libs.ws.WS to make HTTP requests and also play.Play.application.configuration to read the configuration.
Previously, all I needed was some imports like:
import play.api.libs.ws._
import play.api.Play.current
import play.api.libs.concurrent.Execution.Implicits.defaultContext
But now you should use dependency injection to use WS and also to use access the current Play application.
I have something like this (shortened):
abstract class Microservice(serviceName: String) {
// ...
protected lazy val serviceURL: String = play.Play.application.configuration.getString(s"microservice.$serviceName.url")
// ...and functions using WS.url()...
}
An object looks something like this (shortened):
object HelloWorldService extends Microservice("helloWorld") {
// ...
}
Unfortunately I don't understand how I get all the stuff (WS, configuration, ExecutionContect) into the abstract class to make it work.
I tried to change it to:
abstract class Microservice #Inject() (serviceName: String, ws: WSClient, configuration: play.api.Configuration)(implicit context: scala.concurrent.ExecutionContext) {
// ...
}
But this doesn't solve the problem, because now I have to change the object too, and I can't figure out how.
I tried to turn the object into a #Singleton class, like:
#Singleton
class HelloWorldService #Inject() (implicit ec: scala.concurrent.ExecutionContext) extends Microservice ("helloWorld", ws: WSClient, configuration: play.api.Configuration) { /* ... */ }
I tried all sorts of combinations, but I'm not getting anywhere and I feel I'm not really on the right track here.
Any ideas how I can use things like WS the proper way (not using deprecated methods) without making things so complicated?
This is more related to how Guice handles inheritance and you have to do exactly what you would do if you were not using Guice, which is declaring the parameters to the superclass and calling the super constructor at your child classes. Guice even suggest it at its docs:
Wherever possible, use constructor injection to create immutable objects. Immutable objects are simple, shareable, and can be composed.
Constructor injection has some limitations:
Subclasses must call super() with all dependencies. This makes constructor injection cumbersome, especially as the injected base class changes.
In pure Java, it will means doing something like this:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Dependency injection won't change that and you will just have to add the annotations to indicate how to inject the dependencies. In this case, since Base class is abstract, and then no instances of Base can be created, we may skip it and just annotate Child class:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
#Inject
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Translating to Scala, we will have something like this:
abstract class Base(dep: Dependency) {
// something else
}
class Child #Inject() (anotherDep: AnotherDependency, dep: Dependency) extends Base(dep) {
// something else
}
Now, we can rewrite your code to use this knowledge and avoid deprecated APIs:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient) {
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
#Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient)
extends Microservice("helloWorld", configuration, ws) {
// ...
}
The last point is the implicit ExecutionContext and here we have two options:
Use the default execution context, which will be play.api.libs.concurrent.Execution.Implicits.defaultContext
Use other thread pools
This depends on you, but you can easily inject an ActorSystem to lookup the dispatcher. If you decide to go with a custom thread pool, you can do something like this:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient, actorSystem: ActorSystem) {
// this will be available here and at the subclass too
implicit val executionContext = actorSystem.dispatchers.lookup("my-context")
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
#Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient, actorSystem: ActorSystem)
extends Microservice("helloWorld", configuration, ws, actorSystem) {
// ...
}
How to use HelloWorldService?
Now, there are two things you need to understand in order to proper inject an instance of HelloWorldService where you need it.
From where HelloWorldService gets its dependencies?
Guice docs has a good explanation about it:
Dependency Injection
Like the factory, dependency injection is just a design pattern. The core principle is to separate behaviour from dependency resolution.
The dependency injection pattern leads to code that's modular and testable, and Guice makes it easy to write. To use Guice, we first need to tell it how to map our interfaces to their implementations. This configuration is done in a Guice module, which is any Java class that implements the Module interface.
And then, Playframework declare modules for WSClient and for Configuration. Both modules gives Guice enough information about how to build these dependencies, and there are modules to describe how to build the dependencies necessary for WSClient and Configuration. Again, Guice docs has a good explanation about it:
With dependency injection, objects accept dependencies in their constructors. To construct an object, you first build its dependencies. But to build each dependency, you need its dependencies, and so on. So when you build an object, you really need to build an object graph.
In our case, for HelloWorldService, we are using constructor injection to enable Guice to set/create our object graph.
How HelloWorldService is injected?
Just like WSClient has a module to describe how an implementation is binded to an interface/trait, we can do the same for HelloWorldService. Play docs has a clear explanation about how to create and configure modules, so I won't repeat it here.
But after creating an module, to inject a HelloWorldService to your controller, you just declare it as a dependency:
class MyController #Inject() (service: Microservice) extends Controller {
def index = Action {
// access "service" here and do whatever you want
}
}
In scala,
-> If you do not want to explicitly forward all the injected parameters to the base constructor, you can do it like that :
abstract class Base {
val depOne: DependencyOne
val depTwo: DependencyTwo
// ...
}
case class Child #Inject() (param1: Int,
depOne: DependencyOne,
depTwo: DependencyTwo) extends Base {
// ...
}

Scala Slick Cake Pattern: over 9000 classes?

I'm developing a Play! 2.2 application in Scala with Slick 2.0 and I'm now tackling the data access aspect, trying to use the Cake Pattern.
It seems promising but I really feel like I need to write a huge bunch of classes/traits/objects just to achieve something really simple. So I could use some light on this.
Taking a very simple example with a User concept, the way I understand it is we should have:
case class User(...) //model
class Users extends Table[User]... //Slick Table
object users extends TableQuery[Users] { //Slick Query
//custom queries
}
So far it's totally reasonable. Now we add a "Cake Patternable" UserRepository:
trait UserRepository {
val userRepo: UserRepository
class UserRepositoryImpl {
//Here I can do some stuff with slick
def findByName(name: String) = {
users.withFilter(_.name === name).list
}
}
}
Then we have a UserService:
trait UserService {
this: UserRepository =>
val userService: UserService
class UserServiceImpl { //
def findByName(name: String) = {
userRepo.findByName(name)
}
}
}
Now we mix all of this in an object :
object UserModule extends UserService with UserRepository {
val userRepo = new UserRepositoryImpl
val userService = new UserServiceImpl
}
Is UserRepository really useful? I could write findByName as a custom query in Users slick object.
Let's say I have another set of classes like this for Customer, and I need to use some UserService features in it.
Should I do:
CustomerService {
this: UserService =>
...
}
or
CustomerService {
val userService = UserModule.userService
...
}
OK, those sound like good goals:
Abstract over the database library (slick, ...)
Make the traits unit testable
You could do something like this:
trait UserRepository {
type User
def findByName(name: String): User
}
// Implementation using Slick
trait SlickUserRepository extends UserRepository {
case class User()
def findByName(name: String) = {
// Slick code
}
}
// Implementation using Rough
trait RoughUserRepository extends UserRepository {
case class User()
def findByName(name: String) = {
// Rough code
}
}
Then for CustomerRepository you could do:
trait CustomerRepository { this: UserRepository =>
}
trait SlickCustomerRepository extends CustomerRepository {
}
trait RoughCustomerRepository extends CustomerRepository {
}
And combine them based on your backend whims:
object UserModuleWithSlick
extends SlickUserRepository
with SlickCustomerRepository
object UserModuleWithRough
extends RoughUserRepository
with RoughCustomerRepository
You can make unit-testable objects like so:
object CustomerRepositoryTest extends CustomerRepository with UserRepository {
type User = // some mock type
def findByName(name: String) = {
// some mock code
}
}
You are correct to observe that there is a strong similarity between
trait CustomerRepository { this: UserRepository =>
}
object Module extends UserRepository with CustomerRepository
and
trait CustomerRepository {
val userRepository: UserRepository
import userRepository._
}
object UserModule extends UserRepository
object CustomerModule extends CustomerRepository {
val userRepository: UserModule.type = UserModule
}
This is the old inheritance/aggregation tradeoff, updated for the Scala world. Each approach has advantages and disadvantages. With mixing traits, you will create fewer concrete objects, which can be easier to keep track of (as in above, you only have a single Module object, rather than separate objects for users and customers). On the other hand, traits must be mixed at object creation time, so you couldn't for example take an existing UserRepository and make a CustomerRepository by mixing it in -- if you need to do that, you must use aggregation. Note also that aggregation often requires you to specify singleton-types like above (: UserModule.type) in order for Scala to accept that the path-dependent types are the same. Another power that mixing traits has is that it can handle recursive dependencies -- both the UserModule and the CustomerModule can provide something to and require something from each other. This is also possible with aggregation using lazy vals, but it is more syntactically convenient with mixing traits.
Check out my recently published Slick architecture cheat sheet. It does not abstract over the database driver, but it is trivial do change it that way. Just wrap it in
class Profile(profile: JdbcProfile){
import profile.simple._
lazy val db = ...
// <- cheat sheet code here
}
You do not need the cake pattern. Just put it all in one file and you get away without it. The cake pattern allows you to split the code into different files, if you are willing to pay the syntax overhead. People also use the cake pattern to create different configurations including different combinations of services, but I don't think this is relevant to you.
If the repeated syntax overhead per database table bothers you, generate the code. The Slick code-generator is customizable exactly for that purpose:
If you want to blend hand-written and generated code, either feed the hand-written code into the code-generator or use a scheme, where the generated code inherits from hand-written cor vise-versa.
For replacing Slick by something else, replace the DAO methods with queries using another library.

Question about Cake Pattern

Let there a few separate DAO classes OrderDAO, ProductDAO, and CustomerDAO that store/retrieve data in the database and share a single instance DataSource (the database connection factory).
In order to create a DataSource instance and plug it in DAOs we usually use Spring DI. Now I would like to do that in Scala without any DI framework.
I've read about the cake pattern, and it looks like I should do the following:
trait DatabaseContext { val dataSource:Datasource }
trait OrderDAO {this:DatabaseContext =>
... // use dataSource of DatabaseContext
}
trait ProductDAO {this:DatabaseContext =>
... // use dataSource of DatabaseContext
}
object DAOImpl extends OrderDAO with ProductDAO with DatabaseContext {
val dataSource = ... // init the data source
}
Do I understand the cake pattern correctly?
Can I implement these DAOs differently using the cake pattern ?
What does it provide that DI frameworks like Spring do not ?
How can I create separate OrderDAOImpl and ProductDAOImpl objects sharing the same DataSource instance instead of one big DAOImpl?
The advantages of the cake pattern are:
Unlike configuration-file-based DI solutions, matching contracts to
implementations is done at compile time, which reduces class-finding
and compatibility issues. However, many DI engines have an
alternative in-code configuration feature
No third-party libraries
are used. Self-type annotations which let you use the pattern are a
native language feature. No special syntax is used to retrieve the
implementation of the contract
Forgetting to specify an
implementation for a component needed by another component results in
a runtime error - just check this article
http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html
and try not specifying one of the components or specifying a
trait instead of a concrete class in any of the cake pattern
examples or even forgetting to initialize a val corresponding to a component needed
However, to experience these advantages, you need to more strictly adhere to the architecture of the pattern - check the same article and note the wrapping traits that contain the actual contracts and implementations.
Your examples do not seem to be strictly the cake pattern. In your case you could've just used inheritance to create implementations for your traits and use separate classes for each DAO component. In the cake pattern the consuming code would be a component just like the DAO code, and the code assembling the dependencies together would stand alone from it.
To illustrate the cake pattern, you would have to add consuming classes (domain layer or UI layer) to your example. Or it the case your DAO components accessed each other's features you could illustrate the cake pattern on you DAO alone.
to make it short,
trait OrderDAOComponent {
val dao: OrderDAO
trait OrderDAO {
def create: Order
def delete(id: Int): Unit
//etc
}
}
trait OrderDAOComponentImpl extends OrderDAOComponent {
class OrderDAOJDBCImpl extends OrderDAO {
def create: Order = {/*JDBC-related code here*/}
def delete(id: Int) {/*JDBC-related code here*/}
//etc
}
}
//This one has a dependency
trait OrderWebUIComponentImpl {
this: OrderDAOComponent =>
class OrderWebUI {
def ajaxDelete(request:HttpRequest) = {
val id = request.params("id").toInt
try {
dao.delete(id)
200
}
catch {
case _ => 500
}
}
}
}
//This matches contracts to implementations
object ComponentRegistry extends
OrderDAOComponentImpl with
OrderWebUIComponentImpl
{
val dao = new OrderDAOJDBCImpl
val ui = new OrderWebUI
}
//from some front-end code
val status = ComponentRegistry.ui.ajaxDelete(request)
More on your example. I think it could be more like cake if:
trait DatabaseContext { val dataSource:Datasource }
trait OrderDAOComponent {this:DatabaseContext =>
trait OrderDAOImpl {
... // use dataSource of DatabaseContext
}
}
trait ProductDAOComponent {this:DatabaseContext =>
trait ProductDAOImpl {
... // use dataSource of DatabaseContext
}
}
object Registry extends OrderDAOComponent with ProductDAOComponent with DatabaseContextImpl {
val dataSource = new DatasourceImpl //if Datasource is a custom trait, otherwise wrapping needed
val orderDAO = new OrderDAOImpl
val productDAO = new ProductDAOImpl
}
//now you may use them separately
Registry.orderDAO.//
Maybe:
Statically checked at compile time.

Jonas Bonér's dependency injection strategy seems limiting--but maybe I don't understand it

I've read this article several times:
http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html
I think that I get it. However there's something that I'm not quite getting.
Looking at his UserService example, I see that he's set up UserRepositoryComponent to encapsulate UserRepository. But what I don't understand is why UserRepositoryComponent plays two roles: it encapsulates UserRepository and also offers a reference to a UserRepository object.
I'm trying to imagine how I would use this pattern if I wanted to create a service that depends on two UserRepository instances. Maybe the job of the new service is to copy users from a "source" UserRepository to a "destination" UserRepository. So I'm imagining something like this:
trait CopyUserServiceComponent {
val source: UserRepositoryComponent
val destination: UserRepositoryComponent
class CopyUserServiceComponent {
...
}
}
But this is different from the original pattern. In this case I'm defining the dependencies in the component itself instead of inheriting them from some other component. But this seems to me to be the right way to do it: the components should declare their dependencies, not instances of their included services.
What am I missing here?
In this case I'm defining the dependencies in the component itself instead of inheriting them from some other component.
The cake pattern doesn't use inheritance to declare dependencies. Did you see any "extend" in UserServiceComponent?
But this seems to me to be the right way to do it: the components should declare their dependencies, not instances of their included services.
But that's exactly what the cake pattern does: declare dependencies! Perhaps if the example contained def userRepositoryFactory = new UserRepository instead of val userRepository = new UserRepository, that would have been more clear?
So, let's go back to your example:
trait CopyUserServiceComponent {
val source: UserRepositoryComponent
val destination: UserRepositoryComponent
class CopyUserServiceComponent {
...
}
}
Let's see the things we can't do with that:
trait CopyUserServiceComponent {
// The module will need to see my internals!
private val source: UserRepositoryComponent
private val destination: UserRepositoryComponent
class CopyUserServiceComponent {
...
}
}
trait CopyBigUserServiceComponent extends CopyServiceComponent {
// Any change in implementation will have to be reflected in the module!
val tmp: UserRepositoryComponent
...
}
On the other hand...
trait UserRepositoryComponent {
val userRepositoryFactory: () => UserRepository
class UserRepository {
...
}
}
trait CopyUserServiceComponent {
self: UserRepositoryComponent =>
// No problem here
private val source: UserRepository = userRepositoryFactory()
private val destination: UserRepository = userRepositoryFactory()
class CopyUserServiceComponent {
...
}
}
trait CopyBigUserServiceComponent extends CopyServiceComponent {
self: UserRepositoryComponent =>
// No problem here either
val tmp: : UserRepository = userRepositoryFactory()
...
}
EDIT
Complementating the answer, let's consider two different needs:
I need many instances of UserRepository.
In this case, you are applying the pattern at the wrong level. In Jonas' example, UserRepository is at the level of a factory-providing singleton.
So, in that case, you wouldn't do UserRepository and UserRepositoryComponent but, say, UserRepositoryFactory and UserRepositoryFactoryComponent.
I need precisely two singleton UserRepository.
In this case, just do something like this:
trait UserRepositoryComponent {
val sourceUserService: UserService
val destinationUserService: UserService
class UserService ...
}
I assume, Jonas in his article refers to a widely accepted methodology of building scalable applications called Composite Software Construction that in a few words can be explained as follows: entire application (orchestrated on a meta-level) is an assembly built of independend components, which in their turn are compositions of other components and services. In terms of composite software, 'cake' (ComponentRegistry object in the example) is an assembly of components ('UserServiceComponent' and 'UserRepositoryComponent') etc. Whilst in the example components enclose service implementations it can hardly happen in the real-world.
In your example you don't need to define an inner class - you can put your workflow in an ordinary method:
trait CopyUserServiceComponent {
val source: UserRepositoryComponent
val destination: UserRepositoryComponent
def copy = {...}
}
It perfectly conforms to original pattern - essential feature of the cake is not [only] specifying dependencies via self-type annotations, but also ability to abstract from the concrete implementation, till the moment when you need to build an assembly from the components.