Bespoke will use Cake Pattern based DI.
We will split the code in different components, each one with a clearly defined scope.
For each component we will define 3 things
- A trait
trait MyService{
...
}
- A binding key
object MyService{
trait Required {
def myService:MyService
}
}
- An implementation
final class DefaultMyService extends MyService {
...
}
Let's avoid the Impl suffix (as explained here)
- A Binding
object DefaultMyService {
trait Provided extends Required{
final override lazy val myService: MyService = new DefaultMyService()
}
}
Finally, for the application we will have
- Modules
Which are the grouping of multiple bindings
trait MyModule extends DefaultMyService.Provided
with DefaultMyOtherService.Provided
with DefaultMyRepo.Provided
with DefaultMyClient.Provided{
}
- Containers
new MyModule with MyOtherModule with YourModule{}
Binding keys
Binding keys are the key to which a component will be associated.
In our case will be a trait called Required as part of the companion object of the trait we want to bind.
That trait will have one single method (def) without parameter lists, whose return type will be the bound trait, and whose name will be clear and explanatory and not prone to collisions.
It's important that it's a DEF and not a VAL, because order initialization is required when using VAL, which makes it very difficult to get right.
object A{
// NO EXTRA MIXINS
trait Required{
// NO SELF ANNOTATIONS
def a: A // DEF, NOT VAL, NOT LAZY VAL... DEF!
}
}
Bindings
Bindings are defined by a set of dependencies, and a constructor using such dependencies.
In our case it will be a trait in the companion object of out implementation class.
That trait will extend ONLY the Required trait and will implement the method as lazy val (in case of singletons) or def (in case of non singletons).
For good measure a final modifier is encouraged, and for readability an override modifier.
Dependencies will be declared as self annotations, NOT mixins... SELF ANNOTATIONS.
And self annotations to other Required traits.
object DefaultA{
trait Provided extends Required{
self: B.Required with C.Required with D.Required =>
override final lazy val a = new DefaultA(b,c,d)
}
}
SOCIAL SHARE CARD GENERATOR