[MSA] 13. 마이크로서비스로 리팩터링

Jimin Lim·2024년 2월 18일
0

Architecture

목록 보기
23/23

13.1 마이크로서비스 리팩터링 개요

완전히 뜯어고치는 대신 단계적으로 리팩터링하는 것이 좋다.

  • 값 자주 검증: 가치를 일찍 입증할 수 있음
  • 모놀리스 변경 최소화: 서비스 추출 순서 잘 조정
  • 기술 배포 인프라, 모든 것이 필요한 것은 아니다: 테스트 자동화 배포 파이프라인은 꼭 있어야 함

13.2 모놀리스 -> 마이크로서비스 리팩터링 전략

13.2.1 새 기능을 서비스로 구현

기존 모놀리식 애플리케이션에 새 기능이 구현된 코드는 추가하지 말자

  • API 게이트웨이: 새 기능의 요청은 새 서비스로, 기존 요청은 모놀리스로 각각 라우팅
  • 통합 글루 코드: 서비스가 모놀리스 데이터에 접근하고 모놀리스에 구현된 기능을 호출할 수 있게 서비스를 모놀리스에 통합

13.2.2 표현 계층과 백엔드를 분리

애플리케이션은 일반적으로 다음 세 계층으로 구성된다.

  • 표현 계층: HTTP 요청을 처리해 웹 UI에 전달할 HTML 페이지를 생성하는 모듈
  • 비즈니스 로직
  • 데이터 접근 로직: DB, 메시지 브로커 등 인프라 서비스에 접근하는 모듈

-> back - front 나눠라

13.2.3 기능을 여러 서비스로 추출해 모놀리스 분해

서비스로 추출해야 할 기능은 다음과 같다.

  • API 끝점이 구현된 인바운드 어댑터
  • 도메인 로직
  • DB 접근 로직 등이 구현된 아웃바운드 어댑터
  • 모놀리스의 DB 스키마

도메인 모델 분리

위 같은 상황에서 Order를 별도의 서비스로 추출하려면 프로세스 간 객체 참조는 있을 수 없기에 Restaurant을 바라보는 레퍼런스에도 작업이 필요하다.

객체 레퍼런스를 솎아내기 위해 애그리거트 관점으로 생각할 필요가 있다. 레퍼런스를 기본키 필드로 대체 하자.

DB 리팩터링

도메인 모델의 클래스는 대부분 영속적이라 필드가 DB스키마에 매핑되어있다. 따라서 서비스 DB로 옮길 필요가 있다.

데이터를 복제해 DB클라이언트가 새 스키마를 사용하도록 단계적으로 업데이트하는 발상도 있다.

변경 범위를 줄이기 위해 데이터 복제

전이 기간 동안에는 원본 스키마를 유지하되, 원본 스키마와 신규 스키마를 동기화하는 트리거를 사용할 수 있다.

Delivery 엔티티를 추출한다면, 모놀리스 쪽에서는 배달 관련 필드는 읽기 전용으로 두고 배달 서비스의 데이터를 복제해 최신 상태를 유지하도록 한다. 그리고 모놀리스에서 배달 관련 필드를 업데이트하는 코드를 찾아 새 배달 서비스를 호출하도록 변경하면 된다.

어떤 서비스를 언제 추출하냐

기존 모놀리스 개발은 사실상 동결하고 요건이 있을 때마다 서비스를 추출할 것

서비스로 추출하는 것이 이로운 이유는 다음과 같다

  1. 개발 가속화
  2. 성능, 확장성, 신뢰성 문제 해결
  3. 다른 서비스로 추출할 수 있게 만듦: 한 서비스를 추출하면 모둘 간 디펜던시 때문에 다른 서비스의 추출이 단순해지는 경우도 존재한다.

13.3 서비스와 모놀리스 간 협동 설계

서비스와 모놀리스는 서로 호출해야하는 경우가 있다. 따라서 데이터 일관성을 유지하는 것이 중요하다.

13.3.1 통합 글루 설계

IPC를 인터페이스로 캡슐화하자

  • 서비스에서 모놀리스 호출
interface CustomerContactInfoRepository {
    CustomerContactInfo findCustomerContactInfo(long customerId);
}
  • 모놀리스에서 서비스 호출
    -> 서비스 인터페이스 만들어서 ~~

상호 작용 스타일과 IPC 선택

  • RestAPI -> 사실상 동기여서 잘 안씀
  • 데이터 컨슈머가 데이터 레플리카(CQRS 뷰)를 유지하는 방법
    • 데이터 컨슈머는 데이터 프로바이더가 발행한 도메인 이벤트를 구독해 레플리카를 항상 최신 상태로 유지한다. 업데이트는 트랜잭셔널 메시징으로 통신해야 함

부패-방지 계층 구현

도메인 모델은 성격 자체가 달라 서비스-모놀리스가 소통하려면 ACL을 구현해야 한다.

ACL: 상이한 두 도메인 모델이 서로 상대편을 더럽히지 않도록 변환

모놀리스가 도메인 이벤트를 발행/구독하는 방법

모놀리스가 이벤트를 발행/소비하도록 고치기는 쉽지 않다.

모놀리스가 도메인 이벤트를 발행하는 방법은 아래 두 가지가 있다.

  1. 특정 엔터티를 변경하는 코드를 모두 찾아 이벤트 발행 API를 호출하는 코드를 끼워 넣기 -> 쉽지 않다..

  2. DB 수준에서 도메인 이벤트 발행 (트랜잭션 로그 테일링 or 폴링) -> DB 수준에서 이벤트 발행하면 업데이트 사유 파악이 어렵고 비즈니스 이벤트 발행이 어려워진다.

모놀리스에서 구독하는 방법은 쉽다.

  1. 이벤추에이트 트램 같은 fw로 이벤트 핸들러 작성하면 된다.

13.3.2 서비스와 모놀리스에 걸쳐 데이터 일관성 유지

모놀리스에서 보상 트랜잭션 구현은 어렵다.

모놀리스에서 주방 서비스 추출한다고 했을 때 createOrder()을 구현한다고 해보자. 그럼 다음 단계로 구성된 단일 ACID 트랜잭션을 실행한다.

모놀리스

  1. 주문 내역 확인
  2. 주문 가능한 소비자인지 확인
  3. 소비자 신용카드 승인
  4. 주문 생성

사가

  1. 모놀리스: 주문을 APPROVAL_PENDING 상태로 생성, 주문 가능한 소비자인지 확인
  2. 주방 서비스: 주문 내역확인, 티켓을 CREATE_PENDING 상태로 생성
  3. 모놀리스: 신카 승인, 주문상태를 APPROVED
  4. 주방 서비스: 티켓 상태를 AWAITING_ACCEPTANCE

-> 시맨틱 락으로 상태를 계속 두어야 한다. 하지만 기존 모놀리스에 상태를 추가하는 건 쉽지 않은 일 ..

서비스 추출 순서를 조정하면 보상 트랜잭션을 모놀리스에 구현하지 않아도 된다.

위 방식은 보상 트랜잭션이 필요하지만 보상 트랜잭션 없이 설계가 가능하다. 예를 들어 주방 서비스 추출이 아닌 주문 서비스를 추출할 수 있다.

  1. 주문 서비스: APPROVAL_PENDING 주문 생성
  2. 모놀리스: 비즈니스 로직
  3. 주문 서비스: APPROVED

-> 3번이 실패할 일이 없으므로 2번을 롤백시킬 필요가 없다.

13.3.3 인증/인가 처리

마이크로서비스 애플리케이션은 JWT와 같은 토큰 형태로 신원을 전달한다.

  • 모놀리스의 LoginHandler는 USERINFO 쿠키 세팅
  • API 게이트웨이는 USERINFO 쿠키를 검증한 후 이 쿠키를 Authorization 요청 헤더에 넣어 서비스에 전달한다.

13.4 새 기능을 서비스로 구현: 배달 실패 주문 처리

주문 상태를 추적해 약속한 시간에 음식을 배달할 수 없다면 알려야하는 코드를 작성해야 한다. 모놀리스 말고 새 기능을 서비스를 구현하는 방법 알아보자ㅂ!

13.4.1 배달 지연 서비스 설계

  1. getDelayedOrders()로 지연 중/배달 불가 주문 조회 서비스 추가
  2. API 게이트웨이는 위 요청만 라우팅, 나머지는 모놀리스로
  3. 새로운 서비스에서 소유하지 않은 엔터티는 데이터를 복제해옴

13.4.2 배달 지연 서비스를 위한 통합 글루 설계

  • 고객 연락처 데이터: 조회할 일 적고, 크기 작기에 모놀리스에서 api call
  • 주문 상태, 음식점 데이터: 네트워크로 대량 데이터 전송은 비효율적이므로 레플리카
    • 1) 도메인 발행
    • 2) 트랜잭션 로그 테일링
    • -> DB 동기화이므로 1,2 뭐든 좋음
profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글