When an application is instantiated with a Reader
instance, it gets created as a tree of components with distinct instances for the same type:
+-------+
| App |
+-------+
|
+--------------+
+----------| HttpServer |-------+
| +--------------+ |
| |
+-------------------+ +----------------+
| GetCustomersRoute | | GetOrdersRoute |
+-------------------+ +----------------+
| | |
+-----------------+ +-----------------+ +--------------+
| CustomerService | | CustomerService | | PriceService |
+-----------------+ +-----------------+ +--------------+
| | |
+------------+ +------------+ +------------+
| HttpClient | | HttpClient | | HttpClient |
+------------+ +------------+ +------------+
| | |
+------------------+ +------------------+ +------------------+
| HttpClientConfig | | HttpClientConfig | | HttpClientConfig |
+------------------+ +------------------+ +------------------+
The trouble with this setup is that some components might hold onto precious resources, like a thread-pool. In that situation You don’t want them to be duplicated. You can make all the components in your tree become singletons with the singletons
method:
import org.zalando.grafter.syntax.rewriter._
val app: Application =
Application.reader(Config.prod).singletons
This call to singletons
is going to rewrite your application tree into a graph with singletons:
+-------+
| App |
+-------+
|
+--------------+
+----------| HttpServer |------+
| +--------------+ |
| |
+-------------------+ +----------------+
| GetCustomersRoute | | GetOrdersRoute |
+-------------------+ +----------------+
| |
+-----------------+ +--------------+
| CustomerService | | PriceService |
+-----------------+ +--------------+
| |
+------------+ |
| HttpClient |---------------+
+------------+
|
+------------------+
| HttpClientConfig |
+------------------+
The previous rewrite uses the type of components to make singletons. In reality we might want a finer-grained strategy, based on components values. This means that we might first want to set specific values on specific components.
This can be done with the modifyWith
method. For example let’s say HttpClientConfig
contains Uri
parameter pointing to a service we want to access. We want the CustomerService
and the PriceService
to get different configurations:
import org.zalando.grafter.syntax.rewriter._
val app: Application =
Application.reader(Config.prod).
modifyWith[Any] {
case c: CustomerService => c.replace(Config.prod.customersUri)
case c: PriceService => c.replace(Config.prod.pricesUri)
}
This is the resulting graph:
+-------+
| App |
+-------+
|
+--------------+
+----------| HttpServer |-------+
| +--------------+ |
| |
+-------------------+ +----------------+
| GetCustomersRoute | | GetOrdersRoute |
+-------------------+ +----------------+
| | |
+-----------------+ +-----------------+ +--------------+
| CustomerService | | CustomerService | | PriceService |
+-----------------+ +-----------------+ +--------------+
| | |
+------------+ +------------+ +------------+
| HttpClient | | HttpClient | | HttpClient |
+------------+ +------------+ +------------+
| | |
+------------------+ +------------------+ +------------------+
| HttpClientConfig | | HttpClientConfig | | HttpClientConfig |
+------------------+ +------------------+ +------------------+
| uri = "http://c" | | uri = "http://c" | | uri = "http://p" |
+------------------+ +------------------+ +------------------+
The next step is to make singletons across the whole application except for the components having a specific configuration which we want to preserve! This can be done with the singletonsBy
method taking partial functions to make singletons based on values:
import org.zalando.grafter.syntax.rewriter._
lazy val app: Application =
Application.reader(Config.prod).
modifyWith[Any] {
case c: CustomerService => c.replace(Config.prod.customersUri)
case c: PriceService => c.replace(Config.prod.pricesUri)
}.singletonsBy(httpClientSingletons)
lazy val httpClientSingletons: PartialFunction[Any, Any] = {
case c: HttpClient => c.config
case c: HttpClientConfig => c
}
This leads to the final application graph:
+-------+
| App |
+-------+
|
+--------------+
+----------| HttpServer |-------+
| +--------------+ |
| |
+-------------------+ +----------------+
| GetCustomersRoute | | GetOrdersRoute |
+-------------------+ +----------------+
| | |
+-----------------+ | +--------------+
| CustomerService |----------------------+ | PriceService |
+-----------------+ +--------------+
| |
+------------+ +------------+
| HttpClient | | HttpClient |
+------------+ +------------+
| |
+------------------+ +------------------+
| HttpClientConfig | | HttpClientConfig |
+------------------+ +------------------+
| uri = "http://c" | | uri = "http://p" |
+------------------+ +------------------+