paint-brush
Microservices Integration: A Guide to Simplify Project Complexity with Feign Client and POJO Classesby@gromspys
613 reads
613 reads

Microservices Integration: A Guide to Simplify Project Complexity with Feign Client and POJO Classes

by Sergei KorneevOctober 24th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Simplify microservices integration using Feign client & POJO classes. Streamline complex projects effectively.
featured image - Microservices Integration: A Guide to Simplify Project Complexity with Feign Client and POJO Classes
Sergei Korneev HackerNoon profile picture

In the era of microservices, we have to write more and more integrations for their interaction with each other and with third-party systems. Some people create separate libraries with integration and reuse them in several microservices. Some people clutter their code with a huge number of POJO classes; some people create one POJO class with many nested classes. In this article, I would like to share an approach that will help you hide most of the code that prevents you from reading and understanding the project.


For this example, we'll use GitHub API integration and familiar Spring annotations to describe our feign client.


Add dependencies

Let's add feign-spring4 to support client generation based on spring annotations.

<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-spring4</artifactId>
	<version>${feign.version}</version>
</dependency>

Then, let's add feign-Jackson to support the annotations that will be in the POJO-generated classes.

<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-jackson</artifactId>
	<version>${feign.version}</version>
</dependency>

Add a plugin for generating POJO classes

In the plugins section, add the configuration for the jsonschema2pojo-maven-plugin. Specify where you store the JSON schemas, what package name will be used in the generated classes, and disable adding additional fields to ignore those we don't use.

<build>
	<plugins>
		<plugin>
			<groupId>org.jsonschema2pojo</groupId>
			<artifactId>jsonschema2pojo-maven-plugin</artifactId>
			<version>1.2.1</version>
			<configuration>
				<sourceDirectory>${basedir}/src/main/resources/schema</sourceDirectory>
				<targetPackage>com.example.types</targetPackage>
				<includeAdditionalProperties>false</includeAdditionalProperties>
			</configuration>
			<executions>
				<execution>
					<goals>
						<goal>generate</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

Let's describe the JSON scheme that will be used to generate our POJO classes. To prevent the generator from missing classes from definitions, we need to add references to them in properties.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "description" : "Definition of GitHubApi",
  "type": "object",
  "properties" : {
    "contributor" : {
      "type" : "object",
      "$ref" : "#/definitions/Contributor"
    },
    "issue" : {
      "type" : "object",
      "$ref" : "#/definitions/Issue"
    }
  },
  "definitions": {
    "Contributor" : {
      "type" : "object",
      "properties": {
        "login": {
          "type": "string"
        },
        "contributions": {
          "type": "integer"
        }
      }
    },
    "Issue" : {
      "type" : "object",
      "properties": {
        "title": {
          "type": "string"
        },
        "body": {
          "type": "string"
        },
        "labels": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              }
            }
          }
        },
        "user": {
          "type" : "object",
          "$ref" : "#/definitions/User"
        }
      }
    },
    "User" : {
      "type" : "object",
      "properties": {
        "login": {
          "type": "string"
        },
        "avatar_url": {
          "type": "string"
        }
      }
    }
  }
}

To simplify schematic creation, you can use an online schematic generator from JSON. If you want to use your custom classes as part of the response, you can add the following in the definition section and use "$ref": "#/definitions/MyClass":

"MyClass": { 
  "existingJavaType": "com.example.demo.MyClass" 
}

Let’s move on to the integration description.

Let's create a simple interface to describe the final URIs to retrieve the data.

interface GitHubApi {
    @GetMapping("/repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo);

   @GetMapping("/repos/{owner}/{repo}/issues")
    List<Issue> issues(@PathVariable("owner") String owner, @PathVariable("repo") String repo);
}

Create a client to access GitHub. Select JacksonDecoder to support annotations in POJO classes (such as @JsonProperty). Add SpringContract to use spring annotations (@GetMapping, @PathVariable).

GitHubApi github = Feign.builder()
            .decoder(new JacksonDecoder())
            .contract(new SpringContract())
            .target(GitHubApi.class, "https://api.github.com");

We can then use this client to invoke methods such as contributors and issues. More in-depth customization can be done if needed. For example, we can add logging of each request/response, the number of repeated calls in case of errors, and much more.

Conclusion

Of course, there are many code generation implementations that can be used, such as OpenAPI. This approach gives flexibility in describing POJOs and easy client configuration.