Create a microservices application architecture with Spring Boot and Spring Cloud - Part 5 - Business domain services (General microservice)

Introduction

In this part we are continuing the creation of our business domain services.  For this part we will focus on creating the General microservice.

Overview

The business domain services will be regular Spring Boot apps that implement the OpenFeign library in order for each service to also communicate with each other.

Tutorial

General Service

1.)  Create a new Spring Boot app called general_service. Make sure Gradle is used.

2.)  Open up the build.gradle file.

3.)  Remove the content that’s currently in the build.gradle file and then add the following to the file instead:

buildscript {
	ext {
		springBootVersion = '2.1.1.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.blpca'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
	mavenCentral()
	maven { url "https://repo.spring.io/milestone" }
}

ext['springCloudVersion'] = 'Greenwich.RC2'

dependencies {
	compile('org.springframework.cloud:spring-cloud-starter-config')
	compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
	compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
	compile('org.springframework.cloud:spring-cloud-starter-openfeign')
	implementation('org.springframework.boot:spring-boot-starter-jersey')
	implementation('org.springframework.boot:spring-boot-starter-web')
	compile('org.springframework.cloud:spring-cloud-starter-sleuth')
	testImplementation('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}

4.)  Save the file.

5.)  Right click on the project and go to Gradle → Refresh Gradle Project.  You can also use the gradle commands in the command-line to build the project so that it pulls in the dependencies needed. 

 

6.)  Open up GeneralServiceApplication.java.

7.)  In the package import section, import the following packages:

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

8.)  Then below @SpringBootApplication, add the following annotations and resolve any imports as needed:

@EnableDiscoveryClient
@EnableSwagger2
@EnableFeignClients

 

Code Explanation

 

9.)  Next, add the following method to the class so that Swagger will be able to build API documentation for this service:

@Bean
public Docket swaggerApi() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
            .apis(RequestHandlerSelectors.basePackage("com.bryanlor.controllers"))
            .paths(PathSelectors.any())
        .build()
        .apiInfo(new ApiInfoBuilder().version("2.0").title("General API").description("Documentation General API v1.0").build());
}

10.)  Then create a new controller class called, HomeController, with a package signature of com.bryanlor.controllers.

 

11.)  Open up the new controller class you created.

12.)  In the package import section, import the following packages:

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

13.)  Then, above the class declaration, insert the following annotation:

@RestController

 

Code Explanation

  • @RestController
    • A convenience method that combines the following Spring annotations @Controller and @ResponseBody.
    • This annotation will have the controller class automatically serialize return objects for methods into HttpResponse.

 

14.)  Then in the class body, we’re going to add a couple of simple controller methods that return strings:

@GetMapping("/")
public String home() {
    return "This is the homepage.";
}
	
@GetMapping("/{id}")
public String greeting(@PathVariable("id") Long id) {
    return "Hello #" + id + "! ";
}

Code Explanation

  • @GetMapping("/")
    • Mapping all HTTP GET requests for "/" to home() for the General microservice.
  • @GetMapping("/{id}")
    • Mapping all HTTP GET requests for "/{id}" to greeting().
    • {id} is a variable that we are expecting as a part of the URL path that will also be coming in as a parameter for the method.  This is tied to the following method parameter value:
      @PathVariable("id") Long id

       

15.)  Next, we’re going to create a new interface class called, MinnesotaClient, with a package signature of com.bryanlor.clients

16.)  Then we want to import the following packages:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

17.)  Next, above the interface class declaration, add the following annotation:

@FeignClient(name = "minnesota-service")

 

Code Explanation

  • @FeignClient(name = "minnesota-service")
    • This annotation will mark this class as a Feign client, a declarative web service client (REST API client), that will be able to communicate with other clients too.
    • Because we are using Eureka as a discovery service, we can use the designated name for the Minnesota microservice, minnesota-service, as the Feign client name so that the General microservice can easily communicate with it.

 

18.)  Then, let’s add a simple public method that when called will actually make the appropriate call to the Minnesota service:

@GetMapping("/greeting")
public String retrieveMinnesotaGreeting();

Code Explanation

  • @GetMapping("/greeting")
    • We use the annotation, @GetMapping, here on the interface method so that we call the correct path over at the Minnesota microservice. 
    • Think of it like this:
      • 1.)  retrieveMinnesotaGreeting() is called from somewhere within General service.
      • 2.)  MinnesotaClient looks for the following service, minnesota-service, through Eureka and then makes a request to the following path, "/greeting", in the Minnesota service.
      • 3.)  A HTTP response is given by the Minnesota service and returned to the General service.
      • 4.)  Process the response accordingly where it was called in the General service.

 

19.)  Then switch back to the HomeController class.

20.)  In the package import section, import the following packages:

import org.springframework.beans.factory.annotation.Autowired;
import com.bryanlor.clients.MinnesotaClient;

21.)  Then inside the class body, we’re going to create an instance variable and annotate it with the @Autowired annotation:

@Autowired
MinnesotaClient minnesotaClient;

22.)  Next, we’re going to update the greeting method with the following code:

@GetMapping("/{id}")
public String greeting(@PathVariable("id") Long id) {
    return "Hello #" + id + "! " + minnesotaClient.retrieveMinnesotaGreeting();
}

23.)  Create a bootstrap.yml file in /src/main/resources/ if it doesn’t exist yet.

24.)  Open bootstrap.yml file and make sure it has the following properties:

spring:
 application:
   name: general-service
 cloud:
   config:
     uri: http://localhost:8088

25.)  Go back to the Config Server project.

26.)  Create a new file called, general-service.yml, in the following directory,  /src/main/resources/config/, in which we will be inserting some config properties for our General service(s).

27.)  Next, inside general-service.yml, insert the following properties:

server:
 port: 9090
eureka:
 client:
   serviceUrl:
     defaultZone: http://localhost:8061/eureka/

28.)  Now, start/spin up the General and Minnesota services.

29.)  If you head to the following URL in your browser, you should see 2 services in the list of instances that are currently registered with Eureka:

http://localhost:8061/

30.)  Now, if you go to http://localhost:9090/ you should see a response that is returned from the General microservice’s controller method, home.

 

31.)  Then, if you go to http://localhost:9090/89 you should see a different response, but it also contains a response from the Minnesota microservice.

 

32.)  Next, go to http://localhost:9091/greeting and you should see the response from the Minnesota microservice for the endpoint we created for that service.

 

In the next tutorial, we’re going to create a proxy service that serves as our API gateway so that we don’t have to use different domains to reach each different service.  The API gateway will allow us to simple switch the way our URL looks to get to the different services without having to change our domain.

Code Repository

Grab the code at https://github.com/lorbs28/bl-general-service.


Next tutorial part...

Create a microservices application architecture with Spring Boot and Spring Cloud - Part 6 - Proxy service & API gateway