Spring Boot: Microservice Enhanced
In early series of Spring Boot demo we have gone through basics of booting an application using spring boot. In this blog i’ll show you what is microservice pattern and how actually we can implement.
When microservice will be useful
Below is the list of scenario where microservice is useful.
- Divide whole application in workable modules.
- Each module works individually.
- We can deploy it independently.
- Provide balancing among multiple web services.
- Adding of new service will not affect others as it is independently working and loosely coupled.
- Scale your application across horizons.
Goal
Goal of this demo is
- Develop 3 different microservices.
- Deploy all of them in separate environment (for Local we are running on separate port).
- To know how one service will talk with another.
Use case
Here we are creating 3 different microservices. Technically we are creating 3 different project and also will run on different port.
- CartService: Managing Cart Operations, ideally contains mapping between Product and User (Customer)
- ProductService: Expose services related to product.
- UserService: Expose services related to user.
Let’s look all of them in detail.
You can clone repository from below git.
GitHub Repo: https://github.com/yogeshmprajapati/kode12-spring-boot.git
MicroService 1: Product Service
Git Module: spring-boot-microservice-enhanced-product
Runs on: Port 8081
ProductVO.java
1 2 3 4 5 6 7 8 9 10 11 |
package com.kode12.vo; import java.io.Serializable; public class ProductVO implements Serializable{ private long id; private String name; // getters and setters } |
ProductService.java
Service class to serve product detail, here we have hardcoded detail for product 1 and 2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.kode12.service; import com.kode12.vo.ProductVO; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class ProductService { public ProductVO getById(int productId) { ProductVO productVO = new ProductVO(); switch (productId) { case 1: productVO.setId(1); productVO.setName("Apple MacBook Air MMGF2HN/A 13.3-inch Laptop"); break; case 2: productVO.setId(2); productVO.setName("Samsung T3 Portable 500GB USB 3.0 External SSD (MU-PT500B/AM)"); break; } return productVO; } } |
ProductController.java
Controller to expose product data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.kode12.controller; import com.kode12.service.ProductService; import com.kode12.vo.ProductVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/product") public class ProductController { @Autowired ProductService productService; @RequestMapping("/{productId}") public ProductVO getById(@PathVariable("productId") int productId) { return productService.getById(productId); } } |
SpringBootMicroserviceEnhancedProductApplication.java
Class with main method to start an application.
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.kode12; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootMicroserviceEnhancedProductApplication { public static void main(String[] args) { SpringApplication.run(SpringBootMicroserviceEnhancedProductApplication.class, args); } } |
application.properties
1 |
server.port=8081 |
Run MicroService 1 (Product Service)
Url 1: http://localhost:8081/product/1
Use: to get product by id 1.
Output:
1 2 3 4 |
{ "id": 1, "name": "Apple MacBook Air MMGF2HN/A 13.3-inch Laptop" } |
Url 2: http://localhost:8081/product/2
Use: to get product by id 2.
Output:
1 2 3 4 |
{ "id": 2, "name": "Samsung T3 Portable 500GB USB 3.0 External SSD (MU-PT500B/AM)" } |
MicroService 2: User Service
Git Module: spring-boot-microservice-enhanced-user
Runs on: Port 8082
UserVO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.kode12.vo; import java.io.Serializable; public class UserVO implements Serializable { private long id; private String name; public UserVO() { } public UserVO(long id) { this.id = id; } // getters and setters } |
UserService.java
Service class to serve user detail, here we have hardcoded detail for user 1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.kode12.service; import com.kode12.vo.UserVO; import org.springframework.stereotype.Service; @Service public class UserService { public UserVO getById(int userId) { UserVO userVO = new UserVO(); switch (userId) { case 1: userVO.setId(1); userVO.setName("Yogesh P"); break; } return userVO; } } |
Controller to expose user data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.kode12.controller; import com.kode12.service.UserService; import com.kode12.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired UserService userService; @RequestMapping("/{userId}") public UserVO getById(@PathVariable("userId") int userId) { return userService.getById(userId); } } |
SpringBootMicroserviceEnhancedUserApplication.java
Class with main method to start an application.
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.kode12; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootMicroserviceEnhancedUserApplication { public static void main(String[] args) { SpringApplication.run(SpringBootMicroserviceEnhancedUserApplication.class, args); } } |
application.properties
1 |
server.port=8082 |
Run MicroService 2 (User Service)
Url 1: http://localhost:8081/user/1
Output:
1 2 3 4 |
{ "id": 1, "name": "Yogesh P" } |
MicroService 3: Cart Service
Git Module: spring-boot-microservice-enhanced-cart
Runs on: Port 8080
CartVO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.kode12.vo; public class CartVO { private long id; private UserVO user; private ProductVO product; public CartVO() { } public CartVO(long id, UserVO user, ProductVO product) { this.id = id; this.user = user; this.product = product; } // getters and setters } |
ProductVO.java
Same as Microservice 1’s ProductVO.java
UserVO.java
Same as Microservice 2’s UserVO.java
CartService.java
Service class to serve cart detail, here we have hard coded following mapping
Order#1 {User#1, Product#1}
Order#2 {User#1, Product#2}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.kode12.service; import com.kode12.vo.CartVO; import com.kode12.vo.ProductVO; import com.kode12.vo.UserVO; import org.apache.catalina.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class CartService { @Autowired RestTemplate restTemplate; public List<CartVO> getAll(){ List<CartVO> list = new ArrayList<CartVO>(); // Order#1 -> User#1, Product#1 // Order#1 -> User#1, Product#2 Map<String, Object> userUriVariables = new HashMap<String, Object>(); userUriVariables.put("userId", 1); ResponseEntity<UserVO> userResponseEntity = restTemplate.getForEntity("http://localhost:8082/user/{userId}", UserVO.class, userUriVariables); ProductVO productVO1 = restTemplate.getForEntity("http://localhost:8081/product/1", ProductVO.class).getBody(); ProductVO productVO2 = restTemplate.getForEntity("http://localhost:8081/product/2", ProductVO.class).getBody(); list.add(new CartVO(1, userResponseEntity.getBody(), productVO1)); list.add(new CartVO(2, userResponseEntity.getBody(), productVO2)); return list; } } |
Here we have used RestTemplate
to talk with other microservices.
For Ex:
ProductVO productVO1 = restTemplate.getForEntity("http://localhost:8081/product/1", ProductVO.class).getBody();
The code above is used to request product service to provide product detail having product id is 1.
Run MicroService 3 (Cart Service)
Url: http://localhost:8080/cart/getAll
Use: to get all cart records.
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[ { "id": 1, "user": { "id": 1, "name": "Yogesh P" }, "product": { "id": 1, "name": "Apple MacBook Air MMGF2HN/A 13.3-inch Laptop" } }, { "id": 2, "user": { "id": 1, "name": "Yogesh P" }, "product": { "id": 2, "name": "Samsung T3 Portable 500GB USB 3.0 External SSD (MU-PT500B/AM)" } } ] |
Here User Name and Product Name for each user and product are not available in cart service, actually it will be from other microservices.
Issue
After looking into code you can have a doubt that here we have hard coded urls like http://localhost:8081 or http://localhost:8082 and in real time environment it may vary, so how to handle this thing in better way ?
The good answer of this is we need to use some discovery service like Eureka. It is used for middle tier load balancing and having multiple useful feature, i’ll show you how to work with eureka in upcoming posts.
Share current post by copy: https://goo.gl/5oyuwp
Happy Learning!
Thanks,
Yogesh P