본문 바로가기
Spring Cloud

Spring Cloud(MSA) 실습 - 웹 쇼핑몰 3. 주문 서비스 및 테스트

by 장중앙 2021. 9. 16.

https://jangcenter.tistory.com/38에서 이어짐

 

Spring Cloud(MSA) 실습 - 웹 쇼핑몰 2. 카달로그 서비스

https://jangcenter.tistory.com/35에서 이어짐 Spring Cloud(MSA) 실습 - 웹 쇼핑몰 1. 기본 설정과 회원 서비스 실제 MSA를 위한 실습 MSA의 동작 학습/응용이 목적이기 때문에 상세한 기능 구현, 다자인은 신경..

jangcenter.tistory.com

 

주문 서비스(orders Microservice) 설계

기능 apigateway-service URI Method
사용자 상품 주문 등록 /order-service/{userId}/orders POST
사용자 상품 주문 내역 조회 /order-service/{userId}/orders GET

주문 서비스 프로젝트 생성 및 기본설정

* h2의 버전 변경 자동으로 DB생성을 위해, modelmapper 의존성 추가

# porm.xml
.......
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<scope>runtime</scope>
		<version>1.3.176</version>
	</dependency>

	<dependency>
		<groupId>org.modelmapper</groupId>
		<artifactId>modelmapper</artifactId>
		<version>2.3.8</version>
	</dependency>
</dependencies>
.......

application.yml작성

# order-service/application.yml

server:
  port: 0

spring:
  application:
    name: order-service
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true
      path: /h2-console
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb
    username: sa
  jpa:
    hibernate:
      ddl-auto: update-drop

eureka:
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8761/eureka

 

order-service를 apigateway-service에 등록

어플리케이션 이름으로 접근/라우팅을 위해 apigateway-service에 등록

apigateway-service의 application.yml에 order-service의 라우팅 정보를 추가

# apigateway-service/application.yml
....
  routes:
  ....
    - id: order-service
      predicates:
        - Path=/order-service/**
      uri: lb://ORDER-SERVICE
....

order-service 기능 구현

DB를 위한 VO, DTO, JPA 클래스

VO 패키지

// RequestOrder.java
// 주문에 대한 요청처리

package com.example.MSA_web.vo;

import lombok.Data;

@Data
public class RequestOrder {
	private String productId;
	private Integer qty;
	private Integer unitPrice;
}

-----------------------------------------------------------------------------------------------
// ResponseOrder.java
// 응답 처리
package com.example.MSA_web.vo;

import com.fasterxml.jackson.annotation.JsonInclude;

import lombok.Data;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseOrder {
	private String productId;
	private Integer qty;
	private Integer unitPrice;
	private Integer totalPrice;
	
	private String orderId;
	private String userId;
}

DTO 패키지

// OrderDto.java
package com.example.MSA_web.dto;

import java.io.Serializable;

import lombok.Data;

@Data
public class OrderDto implements Serializable {
	private String productId;
	private Integer qty;
	private Integer unitPrice;
	private Integer totalPrice;
	
	private String orderId;
	private String userId;
}

JPA 패키지

// OrderEntity.java
// 실제 DB 데이터

package com.example.MSA_web.jpa;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.ColumnDefault;

import lombok.Data;

@Data
@Entity
@Table(name="orders")
public class OrderEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(nullable=false, length=120, unique=true)
	private String productId;
	
	@Column(nullable=false)
	private Integer qty;
	
	@Column(nullable=false)
	private Integer unitPrice;
	
	@Column(nullable=false)
	private Integer TotalPrice;
	
	@Column(nullable=false)
	private String userId;
	
	@Column(nullable=false, unique=true)
	private String orderId;
	
	@Column(nullable = false, updatable = false, insertable = false)
    @ColumnDefault(value = "CURRENT_TIMESTAMP")
    private Date createdAt;
}

----------------------------------------------------------------------------------------------
// OrderRepository.java

package com.example.MSA_web.jpa;

import org.springframework.data.repository.CrudRepository;

public interface OrderRepository extends CrudRepository<OrderEntity, Long> {
	OrderEntity findByOrderId(String orderId);
	Iterable<OrderEntity> findByUserId(String userId);
}

모듈 기능 구현

controller

// OrderController.java
package com.example.MSA_web.controller;

import java.util.ArrayList;
import java.util.List;

import org.apache.catalina.mapper.Mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 com.example.MSA_web.dto.OrderDto;
import com.example.MSA_web.jpa.OrderEntity;
import com.example.MSA_web.service.OrderService;
import com.example.MSA_web.vo.RequestOrder;
import com.example.MSA_web.vo.ResponseOrder;

@RequestMapping("/order-service")
@RestController
public class OrderController {
	Environment env;
	OrderService orderService;
	
	@Autowired
	public OrderController(Environment env, OrderService orderService) {
		this.env = env;
		this.orderService = orderService;
	}

	@GetMapping("/health_check")
	public String healthCheck() {
		// 할당받은 포트번호 출력
		return String.format("OK ... Port num = %s", env.getProperty("local.server.port"));
	}
	
	@PostMapping("/{userId}/orders")
	public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder order){
		ModelMapper mapper=new ModelMapper();
		// ResponseOrder -> OrderDto
		mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
		OrderDto orderDto =mapper.map(order, OrderDto.class);
		
		// 본문에 저장되지 않은 유저 정보 저장
		orderDto.setUserId(userId);
		// create 서비스 호출
		OrderDto resultDto=orderService.createOrder(orderDto);
		
		//OrderDto -> ResponseOrder
		ResponseOrder responseOrder=mapper.map(resultDto, ResponseOrder.class); 
		
		return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
	}
	
	@GetMapping("/{userId}/orders")
	public ResponseEntity<List<ResponseOrder>> getOrders(@PathVariable("userId") String userId){
		Iterable<OrderEntity> orderList= orderService.getOrdersByUserId(userId);
		
		List<ResponseOrder> resultList=new ArrayList<>();
		orderList.forEach(v->{
			resultList.add(new ModelMapper().map(v, ResponseOrder.class));
		});
		return ResponseEntity.status(HttpStatus.OK).body(resultList);
	}	
}

최종 테스트

각각의 연관관계  때무에 Eureka server -> apigateway-service -> user/catalog/order service 순으로 실행

Eureka 서버 확인

Eureka server, apigateway, user/catalog/order service가 전부 동작중인 것을 확인 

 

각 서비스의 MSA 동작 확인 및 시나리오가 성공하는지 테스트

 * 사용자 등록 -> 사용자 조회 -> 상품 조회 -> 주문 등록 -> 주문 조회 -> 추가 주문 -> 주문 조회

POST를 이용해 DB에 저장하기 편하게 하기 위해 Postman을 사용

 

사용자 등록 

POST -- http://localhost:8000/user-service/users

{ "email": "tester@test.com", "name": "테스터", "pwd": "12341234" }


사용자 조회

GET -- http://localhost:8000/user-service/users


상품 조회

GET -- http://localhost:8000/catalog-service/catalogs

 


주문 등록

POST -- http://localhost:8000/order-service/USER_ID/orders

{ "productId": "CAT_01", "unitPrice": 1000, "qty": 20 }


주문 조회

GET -- http://localhost:8000/order-service/USER_ID/orders


추가 주문

POST -- http://localhost:8000/order-service/USER_ID/orders

{ "productId": "CAT_02", "unitPrice": 1500, "qty": 10 }


주문 조회

GET -- http://localhost:8000/USER_ID/orders

 

댓글