Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1ccd346
:recycle: lombok 사용 및 repository 분리
ckswls56 Oct 28, 2024
e125527
:sparkles: stock service 실제로 동작하도록 추가
ckswls56 Oct 28, 2024
cd2a029
:bug: Transaction 적용
ckswls56 Oct 28, 2024
13c56fb
:sparkles: stockController 구현
ckswls56 Oct 28, 2024
b87ffb3
:card_file_box: productId String to Long
ckswls56 Oct 28, 2024
04f88bc
:sparkles: feat : getProductIdByOrderId API
ckswls56 Oct 28, 2024
ab2f5d6
:sparkles: webflux setting
ckswls56 Oct 28, 2024
d1215ce
Update README.md
ckswls56 Oct 29, 2024
1c41553
:sparkles: Stock 서비스에서 Order 서비스 호출하여 정확한 재고 관리
ckswls56 Oct 29, 2024
b26b4c1
:loud_sound: Order Service log
ckswls56 Oct 29, 2024
49674ba
Merge remote-tracking branch 'origin/master'
ckswls56 Oct 29, 2024
f474c91
Create gradle.yml
ckswls56 Oct 29, 2024
d0ab78a
Update gradle.yml
ckswls56 Oct 29, 2024
b69c7b1
Update gradle.yml
ckswls56 Oct 29, 2024
d4bd112
:bug: build.gradle fix
ckswls56 Oct 29, 2024
7bcd6cb
Merge remote-tracking branch 'origin/master'
ckswls56 Oct 29, 2024
d081e06
Update gradle.yml
ckswls56 Oct 29, 2024
94b6a75
Update gradle.yml
ckswls56 Oct 29, 2024
7489f9f
Update gradle.yml
ckswls56 Oct 29, 2024
8b0b07a
Merge pull request #1 from ckswls56/ckswls56-patch-1
ckswls56 Oct 29, 2024
9b89dee
Rename createStockDto.java to CreateStockDto.java
ckswls56 Oct 29, 2024
2f7a455
Update gradle.yml
ckswls56 Oct 29, 2024
2e1882d
Revert "Create gradle.yml"
ckswls56 Oct 29, 2024
8f05b9a
Merge remote-tracking branch 'origin/master'
ckswls56 Oct 29, 2024
8ebb6ec
:bug: workflow update & CreateStockDto Name Fix
ckswls56 Oct 29, 2024
5d2b6ec
Merge remote-tracking branch 'origin/master'
ckswls56 Oct 29, 2024
1e3db5e
:fire: remove dependency
ckswls56 Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Gradle Build

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 7.5
cache-read-only: false # 캐싱을 활성화합니다.

- name: Cache Gradle dependencies
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build Order Service
working-directory: ./order-service
run: ./gradlew build -x test

- name: Build Payment Service
working-directory: ./payment-service
run: ./gradlew build -x test

- name: Build Stock Service
working-directory: ./stock-service
run: ./gradlew build -x test
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@
<img src="https://user-images.githubusercontent.com/76584547/184909421-26ff2788-6037-47c4-bedc-d36186a52672.png"/>
</p>

### 실제 재고 서비스 구현

#### Notion 정리
- https://chanjin0000.notion.site/MSA-12ed19350581803990c9cdef6b0fe24a?pvs=73

#### 변경후 아키텍처
<p align="center">
<img src="https://github.com/user-attachments/assets/b4570fb3-5e3e-425e-a940-d765ce2567f6"/>
</p>
12 changes: 8 additions & 4 deletions order-service/src/main/java/pir/order/api/OrderController.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package pir.order.api;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import pir.order.service.OrderService;

@RestController
Expand All @@ -12,7 +11,12 @@ public class OrderController {
private final OrderService orderService;

@PostMapping("/order")
public void order(@RequestParam String productId){
public void order(@RequestParam Long productId){
orderService.order(productId);
}
@GetMapping("/order/{orderId}")
public Long getProductId(@PathVariable Long orderId){
return orderService.getProductId(orderId);
}

}
20 changes: 10 additions & 10 deletions order-service/src/main/java/pir/order/domain/Order.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package pir.order.domain;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Table(name = "orders")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;

public Order() {
}
private Long productId;

public Order(String productId) {
public Order(Long productId) {
this.productId = productId;
}

public Long getId() {
return id;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pir.order.domain;
package pir.order.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import pir.order.domain.Order;

public interface OrderRepository extends JpaRepository<Order, Long> {
}
11 changes: 9 additions & 2 deletions order-service/src/main/java/pir/order/service/OrderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import pir.order.domain.Order;
import pir.order.domain.OrderRepository;
import pir.order.repository.OrderRepository;
import pir.order.producer.StockProducer;

@Slf4j
Expand All @@ -14,14 +14,21 @@ public class OrderService {
private final OrderRepository orderRepository;
private final StockProducer stockProducer;

public void order(String productId){
public void order(Long productId){
final Order order = new Order(productId);
final Order newOrder = orderRepository.save(order);
stockProducer.order(newOrder.getId());
log.info("{}번 주문번호 생성", newOrder.getId());
log.info("{}번 상품 주문", productId);
}

public void delete(Long orderId){
orderRepository.deleteById(orderId);
log.info("{}번 주문번호 삭제", orderId);
}

public Long getProductId(Long orderId){
Order order = orderRepository.findById(orderId).orElseThrow(()->new IllegalArgumentException("주문번호가 없습니다."));
return order.getProductId();
}
}
4 changes: 4 additions & 0 deletions stock-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.kafka:spring-kafka'
implementation 'mysql:mysql-connector-java'
//webflux
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
22 changes: 22 additions & 0 deletions stock-service/src/main/java/pir/stock/api/StockController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package pir.stock.api;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pir.stock.dto.CreateStockDto;
import pir.stock.service.StockService;

@RestController
@RequiredArgsConstructor
@RequestMapping("/stock")
public class StockController {
private final StockService stockService;

@PostMapping("")
public void createStock(@RequestBody CreateStockDto createStockDto){
stockService.createStock(createStockDto.getProductId(), createStockDto.getAmount());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package pir.stock.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

public static final String BASE_URL = "http://localhost:8080";

@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl(BASE_URL)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ public class OrderCreatedConsumer {
@KafkaListener(topics = "order-create", groupId = "group-01")
public void decrease(Long orderId){
try {
stockService.decrease(orderId);

stockService.getProductIdByOrderService(orderId)
.subscribe(
stockService::decrease,
error -> log.error("error : {}", error.getMessage())
);

stockService.payment(orderId);
}catch (Exception e){
log.error("======== error : {} ======== ", e.getMessage());
log.error("======== [Rollback] stock-rollback, orderId :{} ======== ", orderId);
stockService.rollbackCreatedOrder(orderId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ public class RollbackConsumer {
private final StockService stockService;

@KafkaListener(topics = "stock-rollback", groupId = "group-01")
public void rollbackDecreaseStock(Long orderId){
public void rollbackDecreaseStock(Long orderId) {
log.error("======== [Rollback] stock-rollback, orderId :{} ======== ", orderId);
stockService.increase(orderId);

stockService.getProductIdByOrderService(orderId)
.subscribe(
stockService::increase,
error -> log.error("error : {}", error.getMessage())
);

stockService.rollbackCreatedOrder(orderId);
}

Expand Down
36 changes: 36 additions & 0 deletions stock-service/src/main/java/pir/stock/domain/Stock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package pir.stock.domain;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.DynamicUpdate;

import javax.persistence.*;

@Entity
@Table(name = "stocks")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DynamicUpdate
public class Stock {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false, unique = true)
private Long id;
@Column(name = "product_id", nullable = false)
private Long productId;
@Column(name = "stock")
private int stock;

public Stock(Long productId, int stock) {
this.productId = productId;
this.stock = stock;
}
public void decrease(){
this.stock--;
}
public void increase(){
this.stock++;
}
}
9 changes: 9 additions & 0 deletions stock-service/src/main/java/pir/stock/dto/CreateStockDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pir.stock.dto;

import lombok.Getter;

@Getter
public class CreateStockDto {
private Long productId;
private int amount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pir.stock.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import pir.stock.domain.Stock;

public interface StockRepository extends JpaRepository<Stock, Long> {
}
45 changes: 39 additions & 6 deletions stock-service/src/main/java/pir/stock/service/StockService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,62 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import pir.stock.domain.Stock;
import pir.stock.producer.OrderProducer;
import pir.stock.producer.PaymentProducer;
import pir.stock.repository.StockRepository;
import reactor.core.publisher.Mono;

import javax.transaction.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
public class StockService {
private final PaymentProducer paymentProducer;
private final OrderProducer orderProducer;
private final StockRepository stockRepository;

private final WebClient webClient;

public Mono<Long> getProductIdByOrderService(Long orderId) {
return webClient.get()
.uri("/order/{orderId}", orderId)
.retrieve()
.bodyToMono(Long.class);
}

public void decrease(Long orderId){
log.info("{}번 상품 재고 -1", orderId);
@Transactional
public void decrease(Long productId) {
Stock stock = stockRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("해당 상품이 존재하지 않습니다."));
if (stock.getStock() <= 0) {
log.info("{}번 상품 재고 부족", productId);
throw new IllegalArgumentException("재고가 부족합니다.");
} else {
stock.decrease();
log.info("{}번 상품 재고 -1", productId);
}
}

public void increase(Long orderId){
log.info("{}번 상품 재고 +1", orderId);
@Transactional
public void increase(Long productId) {
Stock stock = stockRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("해당 상품이 존재하지 않습니다."));
stock.increase();
log.info("{}번 상품 재고 +1", productId);
}

public void payment(Long orderId){
public void payment(Long orderId) {
paymentProducer.payment(orderId);
log.info("{}번 주문 결제", orderId);
}

public void rollbackCreatedOrder(Long orderId){
public void rollbackCreatedOrder(Long orderId) {
orderProducer.rollbackCreatedOrder(orderId);
}

public void createStock(Long productId, int amount) {
Stock stock = new Stock(productId, amount);
stockRepository.save(stock);
}
}
Loading