简体   繁体   中英

Kotlin @ConstructorBinding @ConfigurationProperties class for multiple instances

I came across a problematic to which I can't find any nice solution. Some context: we work with several micro-services, most of which use rest clients. We found out that a lot of them will use similar configurations for similar issues (ie resiliency). Naturally, we want to extract common, heavily duplicated, non business code into a library. But here is the thing: How can I extract a @ConstructorBinding @ConfigurationProperties data class in a library (especially if there could be several instances of these classes in the code base that uses the library)?

Here is some example code:

@ConstructorBinding
@ConfigurationProperties(prefix = "rest.client")
data class MyDuplicatedRestClientProperties(
    val host: String,
    val someOtherField: Int,
    val someFieldWithDefaultValue: String = "default value"
)

I would like to import this in a project to configure 2 different REST clients. I tried:

  • Creating an abstract class my ClientProperties would extend. Sadly, I need to expose all the fields of the parent class which doesn't really help with duplication:
abstract class MyAbstractClient(
    val host: String,
    val someOtherField: Int,
    val someFieldWithDefaultValue: String = "default value"
)
@ConstructorBinding
@ConfigurationProperties(prefix = "rest.client")
class MyImplematationClient(
    val host: String,
    val someOtherField: Int,
    val someFieldWithDefaultValue: String = "default value"
): MyAbstractClient(
    host,
    someOtherField,
    someFieldWithDefaultValue
)
  • Instantiating the properties as a @Bean method with the @ConfigurationProperties but this doesn't work well either as it forces me to put fields with @Value in the @Configuration class:
@Configuration
class MyConfigurationClass {

    @Value("${my.client.host}")
    lateinit var host: String

    @Value("${my.client.someOtherField}")
    lateinit var someOtherField: Int

    @Value("${my.client.someFieldWithDefaultValue:default value}")
    lateinit var someFieldWithDefaultValue: String

    @Bean
    @ConfigurationProperties
    fun myClient() = MyDuplicatedRestClientProperties(
        host,
        someOtherField,
        someFieldWithDefaultValue
    )

}

From my experience, you're on a wrong road. Why?

  1. Duplication in microservices is allowed. Code is not too large, it's decoupled and can be easily changed.
  2. From Distributed Systems theory, sharing classes between multiple components it's a bad thing. Why? Because doing this will couple the components via those classes.
  3. A better approach will be to encapsulate all the integration into a specific library such as a REST client. For example, accessing Service A can be done via a service-a-client.jar which will contain the configuration and the integration that is necessary in order to call the Service A and will expose one or multiple interfaces that can be used as Spring Beans .
  4. Putting the configuration into a library gives you no advantage, configurations are not business related, they are somehow synthetic objects and have no value in the architecture.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM