1

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 (i.e. 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
    )

}
Purfakt
  • 101
  • 1
  • 16

1 Answers1

1

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.
Dina Bogdan
  • 4,345
  • 5
  • 27
  • 56
  • Thank you for your answer! While I understand the logic behind these statements, I don't really see them applying to my question. I end up having 3 classes (divided between 2 services) (and soon many more) with duplicated properties all related to Resilience4j (except for the `host` address) which will almost always have the same default values. It would make a lot of sense to me to separate this in a library for better maintenance. Talking about coupling, this is purely Resilience4j related and could be used in any project that needs to configure a resilient client. – Purfakt May 11 '20 at 13:41
  • Resilience4j it's a good tool, but are you sure that at every microservice from your mesh has the same resilience requirements in order to share this configurations? For me, what you are trying to achieve it's overengineering. – Dina Bogdan May 11 '20 at 15:40
  • My library will mainly contain utilities to facilitate the configuration of some classes (ClientProperties and Client) and factorise code that is often repeated. Regarding the properties, yes, most of my services will have the default values, and if one or a couple of them need to change, I would do that in the application.properties. This is why I am trying to find a way to extract these in a library. My example shows 3 fields but in reality there are 20-ish fields in 3 classes. This isn't over engineering, this is mainly a maintenance issue. – Purfakt May 11 '20 at 20:19