Req/Res 방식의 MSA 연동


모노리식 서비스에서 특정 부분을 마이크로서비스로 떼어내고, 모노리스와 마이크로서비스가 Req/Res 방식으로 상호 통신하는 예제를 가이드를 따라 수행합니다.

이벤트스토밍 모델 준비

  • 아래 모델을 새 탭에서 로딩합니다. 모델 링크
  • 브라우져에 모델이 로딩되지 않으면, 우측 상단의 (사람모양) 아바타 아이콘을 클릭하여 깃헙(Github) 계정으로 로그인 후 리로드하면 아래처럼 랩에 필요한 이벤트스토밍 기본 모델이 출력됩니다.
  • 로딩된 모델은 우측 팔레트 영역에 스티커 목록이 나타나지 않습니다. 상단 메뉴영역에서 포크 아이콘(FORK)을 클릭해 주어진 모델을 복제합니다.

image

  • 우측 팔레트 영역에 스티커 목록들이 나타나는 것이 확인됩니다.

기존 Monolith에서 일부 영역을 마이크로서비스로 분리

본 랩에 주어진 모델을 활용하여 가이드에 따라 모노리스에서 상품서비스를 분리하는 모델링을 수행합니다.

이벤트스토밍

  • monolith 바운디드 컨텍스트를 주문 도메인 스티커에만 한정
  • 새로운 bounded context를 추가하고 이름을 "inventory"로 설정
  • inventory aggregate 객체들을 묶음 선택하여 inventory bounded context 내로 이동
image
  • 재고량을 감소시키는 Command 추가 : inventory BC 내에 Command 스티커를 추가하고, 아래 커맨드 이름을 복사하여 사용합니다.
decrease stock
  • 이때 Command 스티커는 Inventory Aggregate 스티커의 왼쪽에 인접하게 부착합니다.
  • Command 의 설정: "decrease stock" command 를 더블클릭한 후, Method Type을 Extend Verb URI를 선택하고 Attribute로 type: Integer, name: qty를 추가해 줍니다.
  • 속성 추가후, Add Attribute 를 클릭하거나 엔터키로 설정을 확인합니다.
image
  • 원격 호출선 연결: monolith 내의 OrderPlaced Event 스티커와 inventory 의 decrease stock Command 스티커를 연결. 이때 Req/res 라는 표시가 나타남.
image

Code 생성 및 내 Git Repository에 Push

  • 모델링 메뉴의 CODE > Code Preview를 클릭합니다.
  • 상단의 Push to Git 메뉴를 클릭해 나타나는 다이얼로그 박스에서 Create New Repository를 선택하고, CREATE를 클릭합니다.
  • 초기 Github 계정으로 로그인 하였으므로, 나의 Git 정보가 자동으로 표시됩니다.

image

  • 모델 기반 코드가 내 Github에 Push됩니다.

image

  • 좌측 메뉴 IDE를 누른다음, Cloud IDE 목록에서 Open GitPod를 클릭합니다.

호출 측 소스코드의 확인

  • Cloud IDE상에 로딩된 코드 목록에서 아래 리소스를 찾아 봅니다.
  • monolith/../ Order.java 의 @PostPersist 내에 호출을 위해 생성된 샘플코드를 확인합니다:
@PostPersist
public void onPostPersist() {
    labshopmonolith.external.DecreaseStockCommand decreaseStockCommand = new labshopmonolith.external.DecreaseStockCommand();

  // 주문수량 정보를 커맨드 객체에 적재합니다. 
    decreaseStockCommand.setQty(getQty()); 
    
  // InventoryService Proxy를 통해 커맨드 객체와 함께 원격호출 합니다.
    MonolithApplication.applicationContext
        .getBean(labshopmonolith.external.InventoryService.class)
        .decreaseStock((Long.valueOf(getProductId())), decreaseStockCommand);
}

우리는 decreaseStock stub 메서드를 로컬 객체를 호출하는 것처럼 호출하지만 실제적으로는 inventory 원격객체를 호출합니다.

재고량 수정을 위하여 qty 값을 전달하는 Command 객체와 해당 제품 id 를 path 로 전달하는 첫번째 아규먼트로 productId를 전달합니다.

  • monolith/../ external 패키지 내에 생성된 FeignClient 관련 Stub 코드들을 참고합니다 (InventoryService.java, DecreaseStockCommand.java, Inventory.java)
@FeignClient(name = "inventory", url = "${api.url.inventory}")
public interface InventoryService {
    @RequestMapping(
        method = RequestMethod.PUT,
        path = "/inventories/{id}/decreasestock"
    )
    public void decreaseStock(
        @PathVariable("id") Long id,
        @RequestBody DecreaseStockCommand decreaseStockCommand
    );

}

FeignClient 는 실제로는 inventory 원격객체를 호출하는 proxy 객체를 생성합니다.

application.yaml 의 api.url.inventory 설정값의 url 로 PUT 메서드를 해당 path 로 호출하는 원격 호출의 구현체가 채워집니다.

피호출 측 소스코드의 확인과 구현

  • inventory/.. /infra/InventoryController.java
public class InventoryController {

    @Autowired
    InventoryRepository inventoryRepository;

    @RequestMapping(value = "inventories/{id}/decreasestock", method = RequestMethod.PUT, produces = "application/json;charset=UTF-8")
    public Inventory decreaseStock(
        @PathVariable(value = "id") Long id,
        @RequestBody DecreaseStockCommand decreaseStockCommand,
        HttpServletRequest request,
        HttpServletResponse response
    ) throws Exception {
        System.out.println("##### /inventory/decreaseStock  called #####");
        Optional<Inventory> optionalInventory = inventoryRepository.findById(
            id
        );

        optionalInventory.orElseThrow(() -> new Exception("No Entity Found"));
        Inventory inventory = optionalInventory.get();
        inventory.decreaseStock(decreaseStockCommand);

        inventoryRepository.save(inventory);
        return inventory;
    }
}

decreaseStock 에 대한 원격호출을 받을 수 있는 REST Service Mapping 입니다.

호출을 받으면 Inventory 어그리거트의 decreaseStock 으로 전달하는 input adapter 역할을 합니다(hexagonal architecture).

실제 비즈니스 로직 (재고량 감소)은 어그리거트 내부에서만 ubiquitous 언어로 구현되어야 합니다.

  • inventory/../Inventory.java 의 구현
    public void decreaseStock(DecreaseStockCommand decreaseStockCommand) {
        setStock(getStock() - decreaseStockCommand.getQty().longValue());  // Copy & Paste this code
    }

Proxy 객체를 통한 동기호출 테스트

inventory 서비스의 테스트

  • inventory 서비스를 기동시키고 httpie 툴을 통해 서비스가 잘 호출되는지 테스트합니다:
cd inventory
mvn spring-boot:run
  • 인벤토리에 테스트할 상품을 먼저 등록하고 사전 검증합니다.
http :8083/inventories id=1 stock=10
http PUT :8083/inventories/1/decreasestock qty=3
http :8083/inventories/1  # stock must be 7

monolith 를 통하여 inventory 동기호출

  • monolith 를 기동시키고 실제 주문을 통하여 inventory 가 호출되는지 확인합니다:
cd monolith
mvn spring-boot:run

#새 터미널
http :8082/orders productId=1 qty=5
http :8083/inventories/1  # stock must be 2

참고

  1. 파일 다운로드 받기: 터미널 열고 > zip -r test.zip ./* 하신후, 생성된 test.zip 을 우클릭 다운로드
uEngine has registered trademarks and uses trademarks. and MSA-Ez is licensed under the GNU General Public License version 3.0.