How to Build a Full stack application with Angular and Spring Cloud Microservices – Part 1

Welcome to the Angular with Spring Cloud Microservices tutorial series. In our previous Angular + Spring Boot Fullstack Application Development Tutorial Series, we have implemented the following so far.

Creating Backend – Spring REST API – Part 1

Creating Backend – Spring REST API – Part 2

Creating Angular 10 Client Application – Part 3

Secure Spring Boot Angular Application with Two Factor Authentication

Dockerize Angular with NGINX and Spring Boot with MySQL using Docker Compose

Deploy Angular, Spring Boot, and MySQL Application to DigitalOcean Kubernetes in 30 mins

Create CI/CD pipeline using GitHub Actions to Build and Deploy Angular Spring Boot App on Kubernetes in 15 mins

Integrate Razorpay Payment Gateway with Angular and Spring Boot Application in 14 Simple Steps

How to Write Junit 5 Test Cases for Spring REST Controller using Mockito

How to Implement Spring Boot Angular User Registration Email Verification

Build a CRUD application with Angular and Spring Boot in 15 Steps

Implement Server-side Pagination with Angular and Spring Boot in 3 Steps

Using Angular Material Datepicker with Custom Date Format

Create Reusable Angular Confirmation Dialog

Introduction

In this series, we are going to decompose our monolith backend application into microservices using Spring Cloud. Before we dive deep, let’s see what are microservices and what Spring Cloud provides to develop them.

What are Microservices?

Microservices are an architectural style in which a single application comprises many loosely coupled and independently deployable smaller services.

What is Spring Cloud?

The distributed nature of microservices brings challenges. Spring Cloud helps you mitigate these with several ready-to-run cloud patterns. It can help with service discovery, load-balancing, circuit-breaking, distributed tracing, and monitoring. It can even act as an API gateway.

In this series, we are gonna implement some of the common Microservices patterns like API Gateway Pattern, Circuit Breaker Pattern, Service Discovery Pattern, and External Configuration using Spring API Cloud Gateway, Resilience4j, Netflix Eureka, and Spring Cloud Config respectively.

What you’ll build

Our Spring Boot Backend application consists of the following REST APIs:

  • User Service
  • Authentication Service
  • Product Service
  • Order Service

Now, we are gonna do the following

  • Decompose User & Authentication service as a single microservice since both of these connect to the same database. Product and Order service will be decomposed into 2 different microservices. It is recommended that each microservice should,
    • Follow the Single Responsibility Principle in which a microservice should have only one responsibility.
    • Have its own database since it should be independent. So that, when one goes down, it will not affect the other microservices.
  • Implement Spring API Cloud Gateway which will act as a single point of entry to external requests. This will take care of authentication and fault tolerance using Resilience4j which is one of the supported implementations of Spring Cloud Circuit Breaker.
  • Implement a Discovery Service using Spring Cloud Netflix Eureka server. Service discovery allows services to find and communicate with each other without hard-coding the hostname and port. Each service has to register with the service registry. So that, microservices can communicate with other microservices with just the instance name of the service using Service Discovery Client.
  • Implement a Config Service which acts as a central place to manage external properties for applications across all environments. Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. So we are gonna use this to store the configurations in a remote git repository and serve them to the microservices.

Project Structure

Spring Cloud Social Login Demo Project Structure

What you’ll need

  • Spring Tool Suite 4 or any other IDE of your choice
  • JDK 11
  • MySQL Server 8
  • node.js

Tech Stack

  • Spring Boot 2, Spring Security 5, and Spring Cloud 2021.0.1
  • Spring Data JPA and Hibernate 5
  • Angular 13 and Bootstrap 4

Design

Anugular Social Login With Spring Cloud

Create Spring Cloud Parent Project

Firstly, let’s create a spring cloud parent project which will include all the other modules as a child.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.6</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javachinna</groupId>
	<artifactId>spring-social-login-cloud-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-social-login-cloud-demo</name>
	<description>Demo project for Spring Boot</description>
	<packaging>pom</packaging>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.1</spring-cloud.version>
	</properties>
	<modules>
        <module>config-service</module>
        <module>discovery-service</module>
        <module>gateway-service</module>
        <module>user-auth-service</module>
        <module>product-service</module>
        <module>order-service</module>
    </modules>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Decompose Monolithic Spring Boot Backend application

Secondly, let’s decompose the Spring Boot backend application into an independent microservice per responsibility.

Existing Project Structure

+---pom.xml
|       
+---src
|   \---main
|       +---java
|       |   \---com
|       |       \---javachinna
|       |           |   DemoApplication.java
|       |           |   
|       |           +---config
|       |           |       WebConfig.java
|       |           |       AppProperties.java
|       |           |       CurrentUser.java
|       |           |       RestAuthenticationEntryPoint.java
|       |           |       SetupDataLoader.java
|       |           |       WebSecurityConfig.java
|       |           |       
|       |           +---controller
|       |           |       AuthController.java
|       |           |       UserController.java
|       |           |       
|       |           +---dto
|       |           |       ApiResponse.java
|       |           |       JwtAuthenticationResponse.java
|       |           |       LocalUser.java
|       |           |       LoginRequest.java
|       |           |       SignUpRequest.java
|       |           |       SocialProvider.java
|       |           |       UserInfo.java
|       |           |       
|       |           +---exception
|       |           |   |   BadRequestException.java
|       |           |   |   OAuth2AuthenticationProcessingException.java
|       |           |   |   ResourceNotFoundException.java
|       |           |   |   UserAlreadyExistAuthenticationException.java
|       |           |   |   
|       |           |   \---handler
|       |           |           RestResponseEntityExceptionHandler.java
|       |           |           
|       |           +---model
|       |           |       Role.java
|       |           |       User.java
|       |           |       
|       |           +---repo
|       |           |       RoleRepository.java
|       |           |       UserRepository.java
|       |           |       
|       |           +---security
|       |           |   +---jwt
|       |           |   |       TokenAuthenticationFilter.java
|       |           |   |       TokenProvider.java
|       |           |   |       
|       |           |   \---oauth2
|       |           |       |   CustomOAuth2UserService.java
|       |           |       |   CustomOidcUserService.java
|       |           |       |   HttpCookieOAuth2AuthorizationRequestRepository.java
|       |           |       |   OAuth2AccessTokenResponseConverterWithDefaults.java
|       |           |       |   OAuth2AuthenticationFailureHandler.java
|       |           |       |   OAuth2AuthenticationSuccessHandler.java
|       |           |       |   
|       |           |       \---user
|       |           |               FacebookOAuth2UserInfo.java
|       |           |               GithubOAuth2UserInfo.java
|       |           |               GoogleOAuth2UserInfo.java
|       |           |               LinkedinOAuth2UserInfo.java
|       |           |               OAuth2UserInfo.java
|       |           |               OAuth2UserInfoFactory.java
|       |           |               
|       |           +---service
|       |           |       LocalUserDetailService.java
|       |           |       UserService.java
|       |           |       UserServiceImpl.java
|       |           |       
|       |           +---util
|       |           |       CookieUtils.java
|       |           |       GeneralUtils.java
|       |           |       
|       |           \---validator
|       |                   PasswordMatches.java
|       |                   PasswordMatchesValidator.java
|       |                   
|       \---resources
|               application.properties
|               messages_en.properties

User Auth Service

Thirdly, let’s create a new maven project user-auth-service and move the UserController, AuthController and all of its relevant code to this project.

Project Dependencies

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.javachinna</groupId>
		<artifactId>spring-social-login-cloud-demo</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<artifactId>user-auth-service</artifactId>
	<name>user-auth-service</name>
	<description>User and Authentication Service</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-oauth2-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.1</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.1</version>
            <scope>runtime</scope>
        </dependency>
		<!-- mysql driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>dev.samstevens.totp</groupId>
			<artifactId>totp-spring-boot-starter</artifactId>
			<version>1.7.1</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

spring-cloud-starter-netflix-eureka-client dependency provides Netflix eureka service discovery client and spring-cloud-starter-config provides client-side support for externalized configuration.

Remove Authentication

We need to update the WebSecurityConfig to permit all requests without authentication since API Gateway takes care of this.

WebSecurityConfig.java

@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.cors().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable().formLogin().disable().httpBasic().disable()
				.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint()).and().authorizeRequests().anyRequest().permitAll().and().oauth2Login().authorizationEndpoint()
				.authorizationRequestRepository(cookieAuthorizationRequestRepository()).and().redirectionEndpoint().and().userInfoEndpoint().oidcUserService(customOidcUserService)
				.userService(customOAuth2UserService).and().tokenEndpoint().accessTokenResponseClient(authorizationCodeTokenResponseClient()).and()
				.successHandler(oAuth2AuthenticationSuccessHandler).failureHandler(oAuth2AuthenticationFailureHandler);

		// Add our custom Token based authentication filter
		http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
	}

Modify User Controller

Here, we are just gonna append /users to the path so that it will be easy to configure routes in the API Gateway.

UserController.java

package com.javachinna.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.javachinna.config.CurrentUser;
import com.javachinna.dto.LocalUser;
import com.javachinna.util.GeneralUtils;

@RestController
@RequestMapping("/api/users")
public class UserController {

	@GetMapping("/me")
	@PreAuthorize("hasRole('USER')")
	public ResponseEntity<?> getCurrentUser(@CurrentUser LocalUser user) {
		return ResponseEntity.ok(GeneralUtils.buildUserInfo(user));
	}

	@GetMapping("/all")
	public ResponseEntity<?> getContent() {
		return ResponseEntity.ok("Public content goes here");
	}

	@GetMapping("/user")
	@PreAuthorize("hasRole('USER')")
	public ResponseEntity<?> getUserContent() {
		return ResponseEntity.ok("User content goes here");
	}

	@GetMapping("/admin")
	@PreAuthorize("hasRole('ADMIN')")
	public ResponseEntity<?> getAdminContent() {
		return ResponseEntity.ok("Admin content goes here");
	}

	@GetMapping("/mod")
	@PreAuthorize("hasRole('MODERATOR')")
	public ResponseEntity<?> getModeratorContent() {
		return ResponseEntity.ok("Moderator content goes here");
	}
}

Create Main Application Class

UserAuthServiceApplication.java

@EnableEurekaClient annotation enables Eureka discovery configuration for clients. Use this (optionally) in case you want discovery and know for sure that it is Eureka you want. All it does is turn on discovery and let the autoconfiguration find the eureka classes if they are available (i.e. you need Eureka on the classpath as well).

Instead of using @EnableEurekaClient, you can also use the generic @EnableDiscoveryClient annotation which is not specific to any DiscoveryClient implementation. It enables any DiscoveryClient implementation available in the classpath.

package com.javachinna;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(scanBasePackages = "com.javachinna")
@EnableEurekaClient
@EnableJpaRepositories
@EnableTransactionManagement
public class UserAuthServiceApplication extends SpringBootServletInitializer {

	public static void main(String[] args) {
		SpringApplicationBuilder app = new SpringApplicationBuilder(UserAuthServiceApplication.class);
		app.run();
	}

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(UserAuthServiceApplication.class);
	}
}

Modify Application Properties

application.properties

server.port=8084
spring.application.name=user-auth-service
#spring.profiles.active=dev
spring.config.import=configserver:http://localhost:8888
# Database configuration props
spring.datasource.url=jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate props
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
gateway.service.url=http://localhost:8080/login/oauth2/code/{registrationId}
# Social login provider props
spring.security.oauth2.client.registration.google.clientId=<your-client-id>
spring.security.oauth2.client.registration.google.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.google.redirect-uri=${gateway.service.url}
spring.security.oauth2.client.registration.facebook.clientId=<your-client-id>
spring.security.oauth2.client.registration.facebook.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.facebook.redirect-uri=${gateway.service.url}
spring.security.oauth2.client.provider.facebook.user-info-uri=https://graph.facebook.com/me?fields=id,name,email,picture
spring.security.oauth2.client.registration.github.clientId=<your-client-id>
spring.security.oauth2.client.registration.github.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.github.redirect-uri=${gateway.service.url}
spring.security.oauth2.client.registration.linkedin.clientId=<your-client-id>
spring.security.oauth2.client.registration.linkedin.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.linkedin.client-authentication-method=post
spring.security.oauth2.client.registration.linkedin.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.linkedin.scope=r_liteprofile, r_emailaddress
spring.security.oauth2.client.registration.linkedin.redirect-uri=${gateway.service.url}
spring.security.oauth2.client.registration.linkedin.client-name=Linkedin
spring.security.oauth2.client.registration.linkedin.provider=linkedin
spring.security.oauth2.client.provider.linkedin.authorization-uri=https://www.linkedin.com/oauth/v2/authorization
spring.security.oauth2.client.provider.linkedin.token-uri=https://www.linkedin.com/oauth/v2/accessToken
spring.security.oauth2.client.provider.linkedin.user-info-uri=https://api.linkedin.com/v2/me
spring.security.oauth2.client.provider.linkedin.user-name-attribute=id
linkedin.email-address-uri=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
################### GMail Configuration ##########################
spring.mail.host=smtp.gmail.com
spring.mail.port=465
spring.mail.protocol=smtps
[email protected]
spring.mail.password=secret
spring.mail.properties.mail.transport.protocol=smtps
spring.mail.properties.mail.smtps.auth=true
spring.mail.properties.mail.smtps.starttls.enable=true
spring.mail.properties.mail.smtps.timeout=8000
[email protected]
app.client.baseUrl=http://localhost:8081/
    # After successfully authenticating with the OAuth2 Provider,
    # we'll be generating an auth token for the user and sending the token to the
    # redirectUri mentioned by the frontend client in the /oauth2/authorization request.
    # We're not using cookies because they won't work well in mobile clients.
app.oauth2.authorizedRedirectUris=http://localhost:8081/oauth2/redirect,myandroidapp://oauth2/redirect,myiosapp://oauth2/redirect
# For detailed logging during development
#logging.level.com=TRACE
logging.level.web=debug
#logging.level.org.hibernate.SQL=TRACE
#logging.level.org.hibernate.type=TRACE
  • The spring.application.name property is used to specify the application/service name. This is the name that gets registered with Service Discovery. Hence, microservices can communicate with each other using this name instead of hostname and port number.
  • We have moved some common configurations to the remote git repository which will be served by the Config server on http://localhost:8888. So we have configured the spring.config.import property with this URL.
  • Since the gateway service is the entry point for all incoming requests, we need to use the gateway service URL as the redirect-uri for all the social login providers. The gateway service will then forward the request to the user-auth-service.

Product Service

Let’s create a new maven project product-service and move the ProductController and all of its relevant code to this project.

Project Dependencies

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.javachinna</groupId>
		<artifactId>spring-social-login-cloud-demo</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<artifactId>product-service</artifactId>
	<version>1.2.0</version>
	<name>product-service</name>
	<description>Product Service</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Create Main Application Class

ProductServiceApplication.java

package com.javachinna;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(scanBasePackages = "com.javachinna")
@EnableEurekaClient
@EnableJpaRepositories
@EnableTransactionManagement
public class ProductServiceApplication extends SpringBootServletInitializer {

	public static void main(String[] args) {
		SpringApplicationBuilder app = new SpringApplicationBuilder(ProductServiceApplication.class);
		app.run();
	}

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(ProductServiceApplication.class);
	}
}

Create Application Properties

application.properties

server.port=8082
spring.application.name=product-service
# Database configuration props
spring.datasource.url=jdbc:mysql://localhost:3306/products?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate props
spring.jpa.show-sql=true
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

logging.level.web=INFO

Order Service

Let’s create a new maven project order-service and move the OrderController and all of its relevant code to this project.

Project Dependencies

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.javachinna</groupId>
		<artifactId>spring-social-login-cloud-demo</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<artifactId>order-service</artifactId>
	<version>1.2.0</version>
	<name>order-service</name>
	<description>Order Service</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<!-- mysql driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.razorpay</groupId>
			<artifactId>razorpay-java</artifactId>
			<version>1.3.9</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Create Main Application Class

OrderServiceApplication.java

package com.javachinna;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(scanBasePackages = "com.javachinna")
@EnableEurekaClient
@EnableJpaRepositories
@EnableTransactionManagement
public class OrderServiceApplication extends SpringBootServletInitializer {

	public static void main(String[] args) {
		SpringApplicationBuilder app = new SpringApplicationBuilder(OrderServiceApplication.class);
		app.run();
	}

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(OrderServiceApplication.class);
	}
}

Create Application Properties

application.properties

server.port=8083
spring.application.name=order-service
# Database configuration props
spring.datasource.url=jdbc:mysql://localhost:3306/orders?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate props
spring.jpa.show-sql=true
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# For detailed logging during development
#logging.level.com=TRACE
logging.level.web=INFO
#logging.level.org.hibernate.SQL=TRACE
#logging.level.org.hibernate.type=TRACE

What’s next?

In this article, we have decomposed the existing monolithic application into 3 different microservices. In the next article, we are gonna create Spring Cloud Config, API Gateway, and Discovery service.

Leave a Reply