order service는 사용자의 주문과 주문을 확인할 수 있는 서비스를 제공한다.
dependency를 추가하여 프로젝트를 생성해준다.
이전 catalog service와 동일하게
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.176</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.8</version>
</dependency>
dependency를 추가하여 프로젝트 진행에 막히는게 없도록 한다.
application.properties 파일을 수정 또는 삭제 후 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:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
show-sql: true
eureka:
instance:
#instance-id: ${spring.cloud.client.hostname} 으로 작성시 ip 번호로 들어옴
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
client: #eureka에 등록할지에 대한 설정
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka #eureka 서버 정보
logging:
level:
com.example.orderserivce: DEBUG
설정 정보를 넣어준다. catalogs service와 거의 동일한 내용이고 jpa 부분만 수정되었다.
패키지를 다음과 같이 미리 생성해준 뒤 프로젝트를 하나씩 구성해가보겠다.
jpa라는 패키지는 jpa와 프로젝트가 연결되는데 필요한 Entity와 Repository를 설정하는 부분이다.
먼저 OrderEntity class를 생성한 후
@Data
@Entity
@Table(name = "orders")
public class OrderEntity implements Serializable {
@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") //now()
private Date createAt;
}
설정해주고 다음으로는 OrderRepository interface를 생성하여
public interface OrderRepository extends CrudRepository<OrderEntity, Long> {
OrderEntity findByOrderId(String orderId);
Iterable<OrderEntity> findByUserId(String userId);
}
설정을 해준다. 여기서 OrderEntity의 직렬화를 하는 이유는 우리가 작성한 object를 네트워크를 통해 전송하거나 데이터를 저장시키기 위해 byte stream으로 변환하는 과정을 위해 사용한다. 이렇게 하나의 객체를 저장과 전송에 알맞은 형태로 변환하는 과정을 마샬링이라 하고 객체를 전송받은 컴퓨터에서 해석하는 과정을 언마샬링이라 한다.
@Data
public class OrderDto implements Serializable {
private String productId;
private Integer qty;
private Integer unitPrice;
private Integer totalPrice;
private String orderId;
private String userId;
}
ResponseOrder
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseOrder {
private String productId;
private Integer qty;
private Integer unitPrice;
private Integer totalPrice;
private Date createdAt;
private String orderId;
}
RequestOrder
@Data
public class ReqeustOrder {
private String productId;
private Integer qty;
private Integer unitPrice;
}
OrderService
public interface OrderService {
OrderDto createOrder(OrderDto orderDetails);
OrderDto getOrderByOrderId(String orderId);
Iterable<OrderEntity> getAllOrdersByUserId(String userId);
}
OrderServiceImpl
@Service
public class OrderServiceImpl implements OrderService{
OrderRepository orderRepository;
@Autowired
public OrderServiceImpl(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Override
public OrderDto createOrder(OrderDto orderDto) {
orderDto.setOrderId(UUID.randomUUID().toString());
orderDto.setTotalPrice(orderDto.getQty() * orderDto.getUnitPrice());
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
OrderEntity orderEntity = mapper.map(orderDto, OrderEntity.class);
orderRepository.save(orderEntity);
OrderDto returnValue = mapper.map(orderEntity, OrderDto.class);
return returnValue;
}
@Override
public OrderDto getOrderByOrderId(String orderId) {
OrderEntity orderEntity = orderRepository.findByOrderId(orderId);
ModelMapper mapper = new ModelMapper();
OrderDto orderDto = mapper.map(orderEntity, OrderDto.class);
return orderDto;
}
@Override
public Iterable<OrderEntity> getAllOrdersByUserId(String userId) {
return orderRepository.findByUserId(userId);
}
}
@RestController
@RequestMapping("/order-service")
public class OrderController {
OrderService orderService;
@Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@RequestBody ReqeustOrder order, @PathVariable("userId") String userId){
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
OrderDto orderDto = mapper.map(order, OrderDto.class);
orderDto.setUserId(userId);
OrderDto createOrder = orderService.createOrder(orderDto);
ResponseOrder responseOrder = mapper.map(createOrder, ResponseOrder.class);
return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
}
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@RequestBody ReqeustOrder order, @PathVariable("userId") String userId){
Iterable<OrderEntity> orderList = orderService.getAllOrdersByUserId(userId);
List<ResponseOrder> result = new ArrayList<>();
orderList.forEach(v -> {
result.add(new ModelMapper().map(v, ResponseOrder.class));
});
return ResponseEntity.status(HttpStatus.OK).body(result);
}
}
그림과 같이 api gateway에 order service를 추가로 등록해준다.
지금까지 학습한 user service, catalog service, order service를 모두 실행시켜서 Eureka에 정상적으로 등록되는지 확인해보고 postman을 사용하여 실제로 테스트 해보자!
api gateway로 사용하기 때문에 8000번 포트로 user를 정상적으로 등록되는 것을 먼저 확인했다.
그 후 get method로 변경하여 검색 후 userId를 order를 위해 기억해두자.
catalog service도 정상적으로 실행되는 것을 확인하고
위에서 가져온 userId 값과 catalog에 등록된 상품 정보를 갖고 json 데이터로 post 요청을 해본다.
요청 결과가 우리가 지정한 상태 코드 값인 201로 반환되는 것을 확인할 수 있다.
그리고 get method로 다시 요청시 요청했던 주문을 확인할 수 있으며
하나의 주문을 더 생성하여 확인해보면 2개의 주문이 정상적으로 나온다.