├── orderservice ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── ahmedukamel │ │ │ └── orderservice │ │ │ ├── repository │ │ │ └── OrderRepository.java │ │ │ ├── dto │ │ │ ├── InventoryResponse.java │ │ │ ├── OrderRequest.java │ │ │ └── OrderLineItemsDto.java │ │ │ ├── OrderserviceApplication.java │ │ │ ├── configuration │ │ │ └── Configuration.java │ │ │ ├── model │ │ │ ├── OrderLineItems.java │ │ │ └── Order.java │ │ │ ├── controller │ │ │ └── OrderController.java │ │ │ └── service │ │ │ └── OrderService.java │ └── test │ │ └── java │ │ └── com │ │ └── ahmedukamel │ │ └── orderservice │ │ └── OrderserviceApplicationTests.java └── pom.xml ├── productservice ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── ahmedukamel │ │ │ └── productservice │ │ │ ├── repository │ │ │ └── ProductRepository.java │ │ │ ├── ProductserviceApplication.java │ │ │ ├── dto │ │ │ ├── ProductRequest.java │ │ │ └── ProductResponse.java │ │ │ ├── model │ │ │ └── Product.java │ │ │ ├── controller │ │ │ └── ProductController.java │ │ │ └── service │ │ │ └── ProductService.java │ └── test │ │ └── java │ │ └── com │ │ └── ahmedukamel │ │ └── productservice │ │ └── ProductserviceApplicationTests.java └── pom.xml ├── inventoryservice ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── ahmedukamel │ │ │ └── inventoryservice │ │ │ └── InventoryserviceApplicationTests.java │ └── main │ │ └── java │ │ └── com │ │ └── ahmedukamel │ │ └── inventoryservice │ │ ├── dto │ │ └── InventoryResponse.java │ │ ├── repository │ │ └── InventoryRepository.java │ │ ├── model │ │ └── Inventory.java │ │ ├── controller │ │ └── InventoryController.java │ │ ├── service │ │ └── InventoryService.java │ │ └── InventoryserviceApplication.java └── pom.xml ├── apigateway ├── src │ └── main │ │ └── java │ │ └── com │ │ └── ahmedukamel │ │ └── apigateway │ │ └── ApiGatewayApplication.java └── pom.xml ├── discoveryserver ├── src │ └── main │ │ └── java │ │ └── com │ │ └── ahmedukamel │ │ └── discoveryserver │ │ └── DiscoveryServerApplication.java └── pom.xml ├── README.md └── pom.xml /orderservice/src/main/java/com/ahmedukamel/orderservice/repository/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.repository; 2 | 3 | import com.ahmedukamel.orderservice.model.Order; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface OrderRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/repository/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.repository; 2 | 3 | import com.ahmedukamel.productservice.model.Product; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | public interface ProductRepository extends MongoRepository { 7 | } 8 | -------------------------------------------------------------------------------- /orderservice/src/test/java/com/ahmedukamel/orderservice/OrderserviceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class OrderserviceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /inventoryservice/src/test/java/com/ahmedukamel/inventoryservice/InventoryserviceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class InventoryserviceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /apigateway/src/main/java/com/ahmedukamel/apigateway/ApiGatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.apigateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ApiGatewayApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(ApiGatewayApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/dto/InventoryResponse.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @AllArgsConstructor 11 | @RequiredArgsConstructor 12 | public class InventoryResponse { 13 | private String skuCode; 14 | private boolean isInStock; 15 | } 16 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/dto/InventoryResponse.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @AllArgsConstructor 11 | @RequiredArgsConstructor 12 | public class InventoryResponse { 13 | private String skuCode; 14 | private boolean isInStock; 15 | } 16 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/dto/OrderRequest.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @RequiredArgsConstructor 14 | public class OrderRequest { 15 | private List orderLineItemsDtoList; 16 | } 17 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/OrderserviceApplication.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | 8 | public class OrderserviceApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(OrderserviceApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/ProductserviceApplication.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | 8 | public class ProductserviceApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(ProductserviceApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/repository/InventoryRepository.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice.repository; 2 | 3 | import com.ahmedukamel.inventoryservice.model.Inventory; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface InventoryRepository extends JpaRepository { 10 | List findBySkuCodeIn(List skuCode); 11 | } 12 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/dto/ProductRequest.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class ProductRequest { 15 | private String name; 16 | private String description; 17 | private BigDecimal price; 18 | } 19 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/dto/ProductResponse.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class ProductResponse { 15 | private String id; 16 | private String name; 17 | private String description; 18 | private BigDecimal price; 19 | } 20 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/dto/OrderLineItemsDto.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.math.BigDecimal; 10 | 11 | @Data 12 | @Builder 13 | @AllArgsConstructor 14 | @RequiredArgsConstructor 15 | public class OrderLineItemsDto { 16 | private Long id; 17 | private String skuCode; 18 | private BigDecimal price; 19 | private Integer quantity; 20 | } 21 | -------------------------------------------------------------------------------- /discoveryserver/src/main/java/com/ahmedukamel/discoveryserver/DiscoveryServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.discoveryserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class DiscoveryServerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(DiscoveryServerApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/configuration/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.configuration; 2 | 3 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | 7 | @org.springframework.context.annotation.Configuration 8 | public class Configuration { 9 | @Bean 10 | @LoadBalanced 11 | public WebClient.Builder webClientBuilder() { 12 | return WebClient.builder(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/model/Inventory.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import static jakarta.persistence.GenerationType.IDENTITY; 10 | 11 | @Entity 12 | @Table(name = "t_inventory") 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @RequiredArgsConstructor 17 | public class Inventory { 18 | @Id 19 | @GeneratedValue(strategy = IDENTITY) 20 | private Long id; 21 | private String skuCode; 22 | private Integer quantity; 23 | } 24 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.Id; 8 | import org.springframework.data.mongodb.core.mapping.Document; 9 | 10 | import java.math.BigDecimal; 11 | 12 | @Document(value = "product") 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class Product { 18 | @Id 19 | private String id; 20 | private String name; 21 | private String description; 22 | private BigDecimal price; 23 | } 24 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/model/OrderLineItems.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.math.BigDecimal; 10 | 11 | import static jakarta.persistence.GenerationType.IDENTITY; 12 | 13 | @Entity 14 | @Table(name = "t_orders_line_items") 15 | @Data 16 | @Builder 17 | @AllArgsConstructor 18 | @RequiredArgsConstructor 19 | public class OrderLineItems { 20 | @Id 21 | @GeneratedValue(strategy = IDENTITY) 22 | private Long id; 23 | private String skuCode; 24 | private BigDecimal price; 25 | private Integer quantity; 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inventory Management Microservices 2 | 3 | **1. Inventory Service** manage each inventory on product and its capacity.
4 | **2. Order Service** manage all the orders to inventory.
5 | **3. Product Service** manage adding new products.
6 | `Microservices`
`Discovery Server`
`API Gateway` 7 | 8 | ## Tools 9 | 10 | `Java`
`Spring Boot`
`Spring Security`
`Spring Cloud`
`Spring Data JPA`
`Eureka Discovery Server`
`MongoDB`
`PostgreSQL` 11 | 12 | ## Tutorial 13 | 14 | This project is based on tutorial on YouTube and real implementaion, deployment is comming soon in other projects in Microservices pattern. 15 | 16 | ## Contact me 17 | 18 | Created by [@Ahmed Kamel](mailto:ahmedukamel@outlook.com). 19 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/model/Order.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | import static jakarta.persistence.CascadeType.ALL; 12 | import static jakarta.persistence.GenerationType.IDENTITY; 13 | 14 | @Entity 15 | @Table(name = "t_orders") 16 | @Data 17 | @Builder 18 | @AllArgsConstructor 19 | @RequiredArgsConstructor 20 | public class Order { 21 | @Id 22 | @GeneratedValue(strategy = IDENTITY) 23 | private Long id; 24 | private String orderNumber; 25 | @OneToMany(cascade = ALL) 26 | private List orderLineItemsList; 27 | } 28 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.controller; 2 | 3 | import com.ahmedukamel.orderservice.dto.OrderRequest; 4 | import com.ahmedukamel.orderservice.service.OrderService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import static org.springframework.http.HttpStatus.CREATED; 9 | 10 | @RestController 11 | @RequestMapping(value = "api/v1/order") 12 | @RequiredArgsConstructor 13 | public class OrderController { 14 | private final OrderService orderService; 15 | 16 | @PostMapping 17 | @ResponseStatus(CREATED) 18 | public String placeOrder(@RequestBody OrderRequest orderRequest) { 19 | orderService.placeOrder(orderRequest); 20 | return "Order Placed Successfully"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/controller/InventoryController.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice.controller; 2 | 3 | import com.ahmedukamel.inventoryservice.dto.InventoryResponse; 4 | import com.ahmedukamel.inventoryservice.service.InventoryService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import java.util.List; 9 | 10 | import static org.springframework.http.HttpStatus.OK; 11 | 12 | @RestController 13 | @RequestMapping(value = "api/v1/inventory") 14 | @RequiredArgsConstructor 15 | public class InventoryController { 16 | private final InventoryService inventoryService; 17 | 18 | @GetMapping 19 | @ResponseStatus(OK) 20 | public List isInStock(@RequestParam List skuCode) { 21 | return inventoryService.isInStock(skuCode); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/service/InventoryService.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice.service; 2 | 3 | import com.ahmedukamel.inventoryservice.dto.InventoryResponse; 4 | import com.ahmedukamel.inventoryservice.repository.InventoryRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | @RequiredArgsConstructor 13 | public class InventoryService { 14 | private final InventoryRepository inventoryRepository; 15 | 16 | @Transactional(readOnly = true) 17 | public List isInStock(List skuCode) { 18 | return inventoryRepository.findBySkuCodeIn(skuCode) 19 | .stream().map(inventory -> InventoryResponse.builder() 20 | .skuCode(inventory.getSkuCode()) 21 | .isInStock(inventory.getQuantity() > 0) 22 | .build()).toList(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /discoveryserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ahmedukamel 8 | Microservice-full-project 9 | 1.0-SNAPSHOT 10 | 11 | 12 | discoveryserver 13 | 14 | 15 | 20 16 | 20 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-netflix-eureka-server 24 | 25 | 26 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.controller; 2 | 3 | import com.ahmedukamel.productservice.dto.ProductRequest; 4 | import com.ahmedukamel.productservice.dto.ProductResponse; 5 | import com.ahmedukamel.productservice.service.ProductService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | 12 | import static org.springframework.http.HttpStatus.CREATED; 13 | 14 | @RestController 15 | @RequestMapping(value = "api/v1/product") 16 | @RequiredArgsConstructor 17 | public class ProductController { 18 | private final ProductService productService; 19 | 20 | @PostMapping 21 | @ResponseStatus(CREATED) 22 | public void addProduct(@RequestBody ProductRequest productRequest) { 23 | productService.createProduct(productRequest); 24 | } 25 | 26 | @GetMapping 27 | @ResponseStatus(HttpStatus.OK) 28 | public List getAllProducts() { 29 | return productService.getAllProducts(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apigateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ahmedukamel 8 | Microservice-full-project 9 | 1.0-SNAPSHOT 10 | 11 | 12 | apigateway 13 | 14 | 15 | 20 16 | 20 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-gateway 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-eureka-client 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /inventoryservice/src/main/java/com/ahmedukamel/inventoryservice/InventoryserviceApplication.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.inventoryservice; 2 | 3 | import com.ahmedukamel.inventoryservice.model.Inventory; 4 | import com.ahmedukamel.inventoryservice.repository.InventoryRepository; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | @SpringBootApplication 11 | public class InventoryserviceApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(InventoryserviceApplication.class, args); 15 | } 16 | 17 | // @Bean 18 | public CommandLineRunner loadData(InventoryRepository inventoryRepository) { 19 | return args -> { 20 | inventoryRepository.save(Inventory.builder() 21 | .skuCode("iPhone 13") 22 | .quantity(100) 23 | .build()); 24 | 25 | inventoryRepository.save(Inventory.builder() 26 | .skuCode("iPhone 12") 27 | .quantity(0) 28 | .build()); 29 | }; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /productservice/src/main/java/com/ahmedukamel/productservice/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice.service; 2 | 3 | import com.ahmedukamel.productservice.dto.ProductRequest; 4 | import com.ahmedukamel.productservice.dto.ProductResponse; 5 | import com.ahmedukamel.productservice.model.Product; 6 | import com.ahmedukamel.productservice.repository.ProductRepository; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | @Slf4j 16 | public class ProductService { 17 | private final ProductRepository productRepository; 18 | 19 | public void createProduct(ProductRequest productRequest) { 20 | Product product = Product.builder() 21 | .name(productRequest.getName()) 22 | .description(productRequest.getDescription()) 23 | .price(productRequest.getPrice()) 24 | .build(); 25 | 26 | productRepository.save(product); 27 | log.info("Product {'id':'{}', 'name':'{}','description':'{}','price':'{}'} is Saved.", 28 | product.getId(), product.getName(), productRequest.getDescription(), productRequest.getPrice()); 29 | } 30 | 31 | public List getAllProducts() { 32 | return productRepository.findAll().stream().map( 33 | this::mapProductToProductResponse).toList(); 34 | } 35 | 36 | public ProductResponse mapProductToProductResponse(Product product) { 37 | return ProductResponse.builder() 38 | .id(product.getId()) 39 | .name(product.getName()) 40 | .description(product.getDescription()) 41 | .price(product.getPrice()) 42 | .build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /inventoryservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ahmedukamel 8 | Microservice-full-project 9 | 1.0-SNAPSHOT 10 | 11 | 12 | inventoryservice 13 | 14 | 15 | 20 16 | 20 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-netflix-eureka-client 32 | 33 | 34 | com.mysql 35 | mysql-connector-j 36 | runtime 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | true 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /orderservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ahmedukamel 8 | Microservice-full-project 9 | 1.0-SNAPSHOT 10 | 11 | 12 | orderservice 13 | 14 | 15 | 20 16 | 20 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-webflux 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-netflix-eureka-client 36 | 37 | 38 | 39 | com.mysql 40 | mysql-connector-j 41 | runtime 42 | 43 | 44 | org.projectlombok 45 | lombok 46 | true 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-test 51 | test 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /productservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ahmedukamel 8 | Microservice-full-project 9 | 1.0-SNAPSHOT 10 | 11 | 12 | productservice 13 | 14 | 15 | 20 16 | 20 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-mongodb 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-netflix-eureka-client 32 | 33 | 34 | 35 | org.projectlombok 36 | lombok 37 | true 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-test 42 | test 43 | 44 | 45 | 46 | org.testcontainers 47 | mongodb 48 | test 49 | 50 | 51 | org.testcontainers 52 | junit-jupiter 53 | test 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.1.2 10 | 11 | 12 | org.ahmedukamel 13 | Microservice-full-project 14 | 1.0-SNAPSHOT 15 | pom 16 | 17 | inventoryservice 18 | orderservice 19 | productservice 20 | discoveryserver 21 | apigateway 22 | 23 | 24 | 25 | 20 26 | 20 27 | UTF-8 28 | 2022.0.4 29 | 30 | 31 | 32 | 33 | 34 | org.testcontainers 35 | testcontainers-bom 36 | 1.18.3 37 | pom 38 | import 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-dependencies 44 | ${spring-cloud.version} 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | 57 | 58 | 59 | org.projectlombok 60 | lombok 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /productservice/src/test/java/com/ahmedukamel/productservice/ProductserviceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.productservice; 2 | 3 | import com.ahmedukamel.productservice.dto.ProductRequest; 4 | import com.ahmedukamel.productservice.repository.ProductRepository; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.test.context.DynamicPropertyRegistry; 14 | import org.springframework.test.context.DynamicPropertySource; 15 | import org.springframework.test.web.servlet.MockMvc; 16 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 17 | import org.testcontainers.containers.MongoDBContainer; 18 | import org.testcontainers.junit.jupiter.Container; 19 | import org.testcontainers.junit.jupiter.Testcontainers; 20 | 21 | import java.math.BigDecimal; 22 | 23 | import static org.springframework.http.MediaType.APPLICATION_JSON; 24 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 25 | 26 | @SpringBootTest 27 | //@Testcontainers 28 | //@AutoConfigureMockMvc 29 | class ProductserviceApplicationTests { 30 | /* 31 | @Container 32 | static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.2"); 33 | @Autowired 34 | private MockMvc mockMvc; 35 | @Autowired 36 | private ObjectMapper objectMapper; 37 | @Autowired 38 | private ProductRepository productRepository; 39 | 40 | @DynamicPropertySource 41 | static void setProperties(DynamicPropertyRegistry dynamicPropertyRegistry) { 42 | dynamicPropertyRegistry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); 43 | } 44 | 45 | @Test 46 | void shouldCreateProduct() throws Exception { 47 | final String productRequestString = objectMapper.writeValueAsString(getProductRequest()); 48 | mockMvc.perform(MockMvcRequestBuilders.post("api/v1/product") 49 | .contentType(APPLICATION_JSON) 50 | .content(productRequestString)) 51 | .andExpect(status().isCreated()); 52 | 53 | Assertions.assertEquals(1, productRepository.count()); 54 | } 55 | 56 | private ProductRequest getProductRequest() { 57 | return ProductRequest.builder() 58 | .name("iPhone 14") 59 | .description("iPhone 14, Black 256GB") 60 | .price(BigDecimal.valueOf(1200)) 61 | .build(); 62 | } 63 | */ 64 | } 65 | -------------------------------------------------------------------------------- /orderservice/src/main/java/com/ahmedukamel/orderservice/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.ahmedukamel.orderservice.service; 2 | 3 | import com.ahmedukamel.orderservice.dto.InventoryResponse; 4 | import com.ahmedukamel.orderservice.dto.OrderLineItemsDto; 5 | import com.ahmedukamel.orderservice.dto.OrderRequest; 6 | import com.ahmedukamel.orderservice.model.Order; 7 | import com.ahmedukamel.orderservice.model.OrderLineItems; 8 | import com.ahmedukamel.orderservice.repository.OrderRepository; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | import org.springframework.web.reactive.function.client.WebClient; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.UUID; 18 | 19 | @Service 20 | @RequiredArgsConstructor 21 | @Slf4j 22 | @Transactional 23 | public class OrderService { 24 | private final OrderRepository orderRepository; 25 | private final WebClient.Builder webClientBuilder; 26 | 27 | public void placeOrder(OrderRequest orderRequest) { 28 | List orderLineItemsList = orderRequest.getOrderLineItemsDtoList() 29 | .stream().map(this::mapToOrderLineItems).toList(); 30 | 31 | Order order = Order.builder() 32 | .orderNumber(UUID.randomUUID().toString()) 33 | .orderLineItemsList(orderLineItemsList) 34 | .build(); 35 | 36 | // TODO: Call Inventory Service and place Order if Product in Stock 37 | List skuCodes = orderRequest.getOrderLineItemsDtoList() 38 | .stream().map(OrderLineItemsDto::getSkuCode).toList(); 39 | 40 | InventoryResponse[] inventoryResponseArray = webClientBuilder.build().get() 41 | .uri("http://inventory-service/api/v1/inventory", 42 | uriBuilder -> uriBuilder.queryParam("skuCode", skuCodes).build()) 43 | .retrieve() 44 | .bodyToMono(InventoryResponse[].class) 45 | .block(); 46 | 47 | boolean isAllProductInStock = Arrays.stream(inventoryResponseArray != null ? inventoryResponseArray : new InventoryResponse[0]) 48 | .allMatch(InventoryResponse::isInStock); 49 | 50 | if (isAllProductInStock) { 51 | orderRepository.save(order); 52 | log.info("Order Number {} Placed Successfully", order.getOrderNumber()); 53 | } else { 54 | throw new IllegalArgumentException("Product Is Not in Stock"); 55 | } 56 | } 57 | 58 | private OrderLineItems mapToOrderLineItems(OrderLineItemsDto orderLineItemsDto) { 59 | return OrderLineItems.builder() 60 | .id(orderLineItemsDto.getId()) 61 | .skuCode(orderLineItemsDto.getSkuCode()) 62 | .price(orderLineItemsDto.getPrice()) 63 | .quantity(orderLineItemsDto.getQuantity()) 64 | .build(); 65 | } 66 | } 67 | --------------------------------------------------------------------------------