Enjoy My Posts

msa-분해전략

Posted on By Geunwon Lim

이 포스트는 크리스 리처드슨 저의 마이크로서비스 패턴 2장을 보고 작성했습니다. ‘2.2 마이크로서비스 아키텍처 정의’에서는 현재 제가 만들고자 하는 애플리케이션을 예시로 정의해봤습니다. 다 정의하고 보니, 조금 아쉬운 점이 있어서 추후 수정될 것 같긴 합니다.

분해전략

애플리케이션을 기능에 따라 여러 서비스로 분해(서비스 정의)하는 것이 마이크로서비스 아키텍처의 핵심 과제이다. 서비스는 기술 관심사보다는 비즈니스 관심사를 중심으로 구성된다.

2.1 마이크로서비스 아키텍처란 무엇인가?

소프트웨어 아키텍처는 구성 요소 및 그들 간의 디펜던시로 엮인 고수준의 구조물이다. 아키텍처가 중요한 이유는 소프트웨어의 품질 속성, 즉 ‘~성(-ilities)’으로 끝나는 지표가 아키텍처에 의해 결정되기 때문이다. 마이크로서비스 아키텍처는 애플리케이션의 관리성(유지보수성), 테스트성, 배포성을 높이는 아키텍처 스타일이다(부가적으로 확장성도 향상된다).

2.1.1 소프트웨어 아키텍처의 정의와 중요성

컴퓨팅 시스템의 소프트웨어 아키텍처는 소프트웨어 엘리먼트와 그들 간의 관계, 그리고 이 둘의 속성으로 구성된 시스템을 추론하는 데 필요한 구조의 집합이다. <소프트웨어 아키텍처="" 문서화="">, 바스 등

➡︎ 애플리케이션 아키텍처는 여러 파트(엘리먼트)로의 분해와 이런 파트 간의 관계(연관성)이다.

분해가 중요한 이유

  1. 업무와 지식을 분리한다. 덕분에 전문 지식을 보유한 사람들이 함께 생산적으로 애플리케이션 작업을 할 수 있다.
  2. 소프트웨어 엘리먼트가 어떻게 상호 작용하는지 밝힌다.

소프트웨어 아키텍처의 4+1 뷰 모델

4+1 모델은 소프트웨어 아키텍처를 바라보는 상이한 4뷰를 정의한다. 각 뷰는 아키텍처의 특정한 측면을 기술하고 특정 소프트웨어 엘리먼트와 그들 사이의 관계로 구성된다.

  1. 논리 뷰: 개발자가 작성한 소프트웨어 엘리먼트. 객체 지향 언어라면 클래스, 패키지가 해당되며 결국 상속, 연관, 의존 등 클래스와 패키지의 관계를 말한다.
    • 개발자가 작성함.
    • 엘리먼트: 클래스 및 패키지
    • 관계: 클래스와 패키지의 관계
  2. 구현 뷰: 빌드 시스템의 결과물. 모듈(패키징된 코드)과 컴포넌트(하나 이상의 모듈로 구성된 실행/배포 가능 단위)로 구성된다. 자바에서 모듈은 보통 JAR파일, 컴포넌트는 WAR파일이나 실행 가능한 JAR파일이다. 모듈 간 디펜던시와 컴포넌트/모듈 간 조합 관계도 이 뷰에 포함된다.
    • 빌드 시스템이 만들어 냄.
    • 엘리먼트: 모듈(JAR 파일) 및 컴포넌트(WAR 또는 실행 파일)
    • 관계: 모듈/컴포넌트 간 디펜던시
  3. 프로세스 뷰: 런타임 컴포넌트. 각 엘리먼트는 개별 프로세스고 IPC는 프로세스 간 관계를 나타낸다.
    • 컴포넌트 실행
    • 엘리먼트: 프로세스
    • 관계: 프로세스 간 통신
  4. 배포 뷰: 프로세스가 머신에 매핑되는 방법. 이 뷰의 엘리먼트는 (물리 또는 가상) 머신 및 프로세스고, 머신 간의 관계가 바로 네트워킹이다.
    • 머신에서 실행중인 프로세스
    • 엘리먼트: 머신 및 프로세스
    • 관계: 네트워킹
  • 시나리오: 특정 뷰 내에서 얼마나 다양한 아키텍처 요소가 협동하여 요청을 처리하는 지 기술한다. 가령 논리 뷰의 시나리오는 클래스가 협동하는 방법을, 프로세스 뷰의 시나리오는 프로세스가 서로 어떻게 협동하는지 나타낸다. 즉, 뷰를 작동시킨다고 이해하면 된다.

➡︎ 4뷰는 중요한 아키텍처 측면을, 시나리오는 뷰의 여러 엘리먼트가 협동하는 과정을 명시한다.

아키텍처의 중요성

애플리케이션 요건은 크게 두 종류로 나뉜다. 첫째, 애플리케이션이 해야 할 일을 정의하는 기능 요건이다. 이 기능 요건과 아키텍처는 거의 무관하다(어떤 아키텍처든 기능을 구현할 수 있다). 둘째, 이른바 ‘~성’으로 끝나는 서비스 품질 요건이다. 애플리케이션이 요건을 충족시킬 수 있도록 설계돼야 하기 때문에 아키텍처는 중요하다. 즉, 어떤 아키텍처를 선택하느냐에 따라 이러한 품질 요건이 결정된다. 확장성, 신뢰성 같은 런타임 품질 외에도 관리성, 테스트성, 배포성처럼 개발 시점의 품질도 해당된다.

2.1.2 아키텍처 스타일

1. 계층화 아키텍처 스타일

계층마다 명확히 정의된 역할을 분담하고, 계층 간 디펜던시는 아키텍처로 제한한다. 3계층 아키텍처가 대표적인 사례이다.

(1) 표현 계층: 사용자 인터페이스 또는 외부 api가 구현된 계층

(2) 비즈니스 로직 계층: 비즈니스 로직이 구현된 계층

(3) 영속화 계층: DB 상호 작용 로직이 구현된 계층

계층화 아키텍처의 단점

  1. 표현 계층이 하나 뿐이다: 애플리케이션을 호출하는 시스템은 여러개일 수도 있는데…
  2. 영속화 계층이 하나 뿐이다: 애플리케이션이 상호작용하는 DB가 여러개일 수도 있는데…
  3. 비즈니스 로직 계층을 영속화 계층에 의존하는 형태로 정의한다: 이론적으로 이런 디펜던시 때문에 DB 없이 비즈니스 로직을 테스트 하는 것이 불가능하다.
  4. 계층화 아키텍처는 잘 설계된 애플리케이션에서 디펜던시를 잘못 나타낸다.

이러한 문제를 해결하기 위해 육각형 아키텍처가 고안되었다.

2. 육각형 아키텍처 스타일

논리 뷰를 비즈니스 로직 중심으로 구성한다. 애플리케이션에 표현 계층 대신 비즈니스 로직을 호출하여 외부에서 들어온 요청을 처리하는 인바운드 어댑터와 영속형 계층 대신 비즈니스 로직에 의해 호출되고 외부 애플리케이션을 호출하는 아웃바운드 어댑터를 둔다. 비즈니스 로직이 어댑터에 전혀 의존하지 않는다는 것이 이 아키텍처의 가장 중요한 특장점이다. 오히려 어댑터가 비즈니스 로직에 의존한다.

비즈니스 로직에는 하나 이상의 포트가 있다. 포트는 비즈니스 로직이 자신의 외부 세계와 상호 작용하는 방법이 정의된 작업이다. 가령 자바라면 인터페이스가 포트다. 포트 종류는 인바운드/아웃바운드 두 가지이다. 인바운드 포트는 비즈니스 로직이 표출된 API로서, 외부 애플리케이션은 이 API를 통해 비즈니스 로직을 호출한다. 아웃바운드 포트는 비즈니스 로직이 외부 시스템을 호출하는 방법에 관한 것이다. 즉, 비즈니스 로직은 하나 이상의 포트를 지니며, 인바운드 어댑터는 외부 시스템의 요청을 처리하고 인바운드 포트를 호출한다. 아웃바운드 어댑터는 아웃바운드 포트를 통해 외부 시스템을 호출한다. 인바운드 어댑터는 외부에서 들어온 요청을 인바운드 포트를 호출해서 처리한다(예: REST 끝점, 웹 페이지가 구현된 스프링 MVC 컨트롤러, 메시지를 구독하는 메시지 브로커 클라이언트). 동일한 인바운드 포트를 여러 인바운드 어댑터가 호출할 수 있다. 아웃바운드 어댑터는 비즈니스 로직에서 들어온 요청을 외부 애플리케이션/서비스를 호출해서 처리한다(예: DB 작업이 구현된 데이터 접근 객체 클래스, 원격 서비스를 호출하는 프록시 클래스). 아웃바운드 어댑터는 이벤트를 발행하기도 한다.

육각형 아키텍처 스타일의 가장 큰 장점은 비즈니스 로직에 있던 표현/데이터 접근 로직이 어댑터와 분리되었기 때문에 비즈니스 로직이 표현/데이터 접근 로직 어디에도 의존하지 않는다는 점이다. 육각형 아키텍처는 마이크로서비스 아키텍처를 이루는 각 서비스 아키텍처를 기술하는 가장 좋은 방법이다.

2.1.3 마이크로서비스 아키텍처는 일종의 아키텍처 스타일이다

모놀리식 아키텍처는 구현 뷰를 단일 컴포넌트(하나의 실행 파일이나 WAR파일)로 구성한 아키텍처 스타일이다. 모놀리식 애플리케이션은 육각형 아키텍처 방식으로 구성한 논리 뷰를 가질 수 있다.

마이크로서비스 아키텍처도 일종의 아키텍처 스타일이지만, 구현 뷰를 다수의 컴포넌트(여러 실행 파일이나 WAR파일)로 구성하는 차이점이 있다. 컴포넌트는 곧 서비스고, 각 서비스는 자체 논리 뷰 아키텍처를 갖고 있다. 커넥터는 이런 서비스가 서로 협동할 수 있게 해주는 통신 프로토콜이다. 마이크로서비스 아키텍처의 핵심 제약 조건은 서비스를 느슨하게 결합한다는 것이다. 따라서 여러 서비스가 협동하는 방식에도 제약 사항이 있다.

서비스란 무엇인가?

서비스는 어떤 기능이 구현되어 단독 배포가 가능한 소프트웨어 컴포넌트이다. 서비스는 클라이언트가 자신이 서비스하는 기능에 접근할 수 있도록 커맨드, 쿼리, 이벤트로 구성된 API를 제공한다. 서비스 작업은 크게 커맨드와 쿼리로 나뉜다.

서비스 API는 내부 구현 상세를 캡슐화한다. 모놀리스와 달리 개발자는 API를 우회하여 서비스에 접근하는 코드를 작성할 수 없으므로 마이크로서비스 아키텍처에서 애플리케이션 모듈성은 보장된다.

각각의 서비스는 자체 아키텍처를 갖고 있기 때문에 기술 스택을 독자적으로 구축할 수 있지만, 대부분 육각형 아키텍처 형태를 취한다.

마이크로서비스는 기술적 관심사보다 비즈니스 능력, 하위 도메인 등 비즈니스 관심사 위주로 구성된다.

느슨한 결합

서비스는 구현 코드를 감싼 API를 통해서만 상호 작용하므로 클라이언트에 영향을 끼치지 않고 서비스 내부 구현 코드를 바꿀 수 있다. 서비스가 DB 테이블을 서로 공유하지 않기 때문에 런타임 격리도 향상된다. 어떤 서비스가 DB락을 획득하여 다른 서비스를 블로킹하는 일 자체가 불가능하다. DB를 공유하지 않기 때문에 여러 서비스에 걸쳐 데이터를 쿼리하고 일관성을 유지하는 일은 더 복잡해진다는 단점이 있다.

공유 라이브러리의 역할

마이크로서비스 아키텍처에서도 공유 라이브러리를 사용하고픈 유혹에 빠지기 쉬운데, 서비스 코드 중복을 줄이는 것은 좋지만 의도치 않은 서비스 간 결합도를 유발하지 않도록 조심해야 한다. 변경 가능성이 조금이라도 있는 기능이라면 별도의 서비스로 구현하는 것이 낫다. 바뀔 일이 거의 없는 기능은 라이브러리에 담아 쓰는 것이 좋다.

서비스의 규모는 별로 중요하지 않다

마이크로라고 명칭돼있지만, 크기가 중요한 것이 아니다. 크기보다는 작은 팀이 가장 짧은 시간에 다른 팀과 협동하는 분은 최소로 하여 개발 가능한 서비스를 설계해야 한다는 것이 중요하다.

마이크로서비스 아키텍처를 통해 유지보수성, 테스트성, 배포성 등 개발 단계의 품질 속성이 개선된다. 주된 목표는 아니지만 애플리케이션 확장성도 향상된다.

2.2 마이크로서비스 아키텍처 정의

마이크로서비스 아키텍처를 정의하려면, 일단 도메인 전문가가 문서로 정리한 요건들과 기존 어플리케이션을 출발점으로 삼아야 한다. 이후 마이크로서비스 아키텍처를 정의하는 3단계 프로세스에 따라 정의한다. 3단계 프로세스는 기계적이지 않고, 주관이 개입하고 창의성이 필요하다.

2.2.1 마이크로서비스 아키텍처를 정의하는 3단계 프로세스

아키텍처를 정의하는 1단계는 시스템 작업 식별(애플리케이션 요건을 핵심 요청으로 추출하는 것)이다. 시스템 작업은 결국 여러 서비스가 서로 협동하는 방식을 표현한 아키텍처 시나리오가 된다. 출발점은 사용자 스토리 등의 요건이다. 사용자 스토리를 통해 기능 요건을 정의하고, 기능 요건을 통해 시스템 작업을 식별한다. 시스템 작업은 외부 요청을 나타낸다.

2단계는 어떻게 여러 서비스로 분해할 지 결정하는 것이다. 여러가지 전략을 선택할 수 있다. 비즈니스 아키텍처 시각에서 비즈니스 능력에 따라 서비스를 정의할 수도 있고, DDD의 하위 도메인별로 서비스를 구성하는 전략도 가능하다. 어떤 전략을 구사하든 최종 결과는 기술 개념이 아닌 비즈니스 개념 중심으로 이루어진 서비스들이다.

3단계는 서비스별로 API를 정의하는 일이다. 이를 위해 먼저 1단계에서 식별된 시스템 작업을 각 서비스에 배정해야 한다. 이 때 여러 서비스가 협동하는 방식을 결정해야 하는데, 대부분 서비스에 추가 지원 작업을 두는 형태가 될 것이다. IPC도 정해야 한다.

분해(서비스 정의) 과정에서의 장애물

  1. 네트워크 지연: 서비스 간 왕복이 너무 잦아 분해할 수 없는 경우도 있다.
  2. 서비스 간 동기 통신으로 인해 강요성이 떨어진다. 해결책은 자기 완비형 서비스 개념이다.
  3. 여러 서비스에 걸쳐 데이터 일관성을 지키는 요건이다. 보통 사가로 해결한다.
  4. 애플리케이션 도처에 숨어 있는 만능 클래스이다. 보통 DDD개념을 활용해 해결한다.

2.2.2 시스템 작업 식별

시스템 작업 식별엔 사용자 스토리와 이와 관련된 사용자 시나리오에서 출발한다. 비즈니스 로직을 식별한다고 이해해도 될 것 같다. 1단계는 시스템 작업을 기술하기 위해 필요한 보케블러리를 제공하는 핵심 클래스로 구성된 고수준의 도메인 모델을 생성하는 것이다. 2단계는 시스템 작업 식별 후 그 동작을 도메인 모델 관점에서 기술하는 것이다. 도메인 모델은 주로 사용자 스토리의 명사에서 도출한다. 시스템 작업은 주로 동사에서 도출하고, 각각 하나 이상의 도메인 객체와 그들 간의 관계로 기술한다. 시스템 작업은 도메인 모델을 생성, 수정, 삭제하거나 모델 간 관계를 맺고 끊을 수 있다.

0. 기능요건

  1. 사용자로서 현재 활성화되어 있는 채팅방의 목록과 참여자 수를 보고 싶다. : showChatRooms()
  2. 사용자로서 이메일, 이름, 비밀번호를 입력하고 입력한 이메일을 활용하여 본인인증 후 회원가입 하고 싶다. : signUp()
  3. 사용자로서 특정 지역 내에 있을 때 해당 지역의 단체 채팅방에 입장하고 싶다. : join()
  4. 사용자로서 지역의 단체 채팅방에서 다른 사용자들과 메시지를 주고받고 싶다. : send()
  5. 사용자로서 게시판에 글을 남겨 언어 교환을 할 다른 사용자를 찾고 싶다. : post()
  6. 사용자로서 게시판에 작성된 글에 댓글을 남겨 언어 교환을 할 다른 사용자를 찾고 싶다. : comment()
  7. 광고주로서 애플리케이션의 특정 위치에 광고를 남기고 싶다. advertise()

1. 고수준 도메인 모델 생성: 필요한 객체 대략적으로 파악

먼저 기능 요건을 사용자 시나리오로 확장시키자. 시나리오를 보면 필요할 것 같은 클래스를 알 수 있다. 이 후 고수준의 애플리케이션 모델을 대략 그려보자. 최종 모델보다는 훨씬 단순한 모델이다. 도메인 모델은 스토리에 포함된 명사를 분석하고 도메인 전문가와 상담하는 등 표준 기법을 활용하여 생성한다.

예시: User, RegionalChatRoom, Location, ChatMessage, Board, Post, Comment, Advertisement

2. 시스템 작업 정의

웹 애플리케이션에서 요청은 HTTP 기반이지만, 요청 프로토콜은 다양하기 때문에 특정 프로토콜로 제한할 것이 아니라 요청을 나타내는 시스템 작업 개념을 좀 더 추상화 하는 것이 좋다. 시스템 작업은 크게 두 종류로 나뉜다.

  • 커맨드: 데이터 생성, 수정, 삭제

  • 쿼리: 데이터 읽기

시스템 커맨드를 식별하려면 사용자 스토리/시나리오에 포함된 동사를 먼저 분석한다. 커맨드는 매개변수, 반환값, 동작 방식의 명세를 도메인 모델 클래스로 정의한다. 이 명세는 작업 호출 시 충족되어야 할 선행 조건(given), 작업 호출 후 충족되어야 할 후행 조건(then)으로 구성된다. 시스템 작업 호출 시 먼저 선행 조건을 확인한 후, 후행 조건을 만족시키는 액션을 수행한다. 애플리케이션은 커맨드 외에도 사용자가 의사 결정을 하는 데 필요한 정보를 UI에 제공하는 쿼리를 제공해야 한다.

고수준 도메인 모델과 시스템 작업을 보면 애플리케이션이 무슨 일을 하는 지 알 수 있기 때문에 아키텍처를 정의하는 데 유용하다.

시스템작업식별

2.2.3 서비스 정의: 비즈니스 능력 패턴별 분해

시스템 작업을 정의한 후에는 애플리케이션 서비스를 식별한다. 서비스를 기계적으로 식별하는 방법은 없지만 다양한 분해 전략을 선택할 수는 있다. 어떠한 전략을 사용하든 최종 결과는 기술 개념이 아닌 비즈니스 개념 중심으로 서비스가 구성된 아키텍처이다.

첫 번째 전략은 비즈니스 능력에 따라 분해하는 것이다. 비즈니스 능력은 비즈니스가 가치를 생산하기 위해 하는 일을 말한다. 비즈니스 능력은 곧 조직이 하는 일이다. 조직이 비즈니스를 하는 방법은 그때마다 다르고 급격히 변하기도 하지만 비즈니스 능력은 대체로 크게 달라지지 않는다. 비즈니스 능력은 보통 특정 비즈니스 객체에 집중하며, 여러 개의 하위 능력으로 분해할 수 있다. 비즈니스 능력을 식별한 후 능력에 따라 또는 연관된 능력 그룹에 따라 서비스를 정의한다. 여기에서 주관, 의사결정이 필요하다. 능력 체계의 어느 수준을 서비스에 매핑할지는 주관적으로 판단할 문제이다.

비즈니스능력패턴별서비스정의

현재 만들고자 하는 애플리케이션의 비즈니스 능력을 열거하고, 서비스에 매칭시켜봤다. 회계의 경우 상위 능력에 따른 하위 능력이 없었기 때문에 서비스에 배정하기 수월했다. 반면, 나머지는 상위 능력에 매칭되는 하위 능력이 있었기 때문에 고민을 했다. 소비자 관리의 경우 외국인 유학생이나 내국인 학생의 관리가 크게 다르지 않을 것 같아 묶었다. 채팅의 경우 채팅방 관리와 구역 관리가 상이하긴 하지만, 나눌 경우 지나치게 프로세스 간 통신이 많아질 것이 염려되어 묶었다. 다만 이것은 추후 충분히 나눠질 수도 있을 것 같다. 게시판 관리의 경우도 게시판과 댓글이 밀접한 연관이 있고, 나눌 경우 IPC가 지나치게 많아질 것을 우려하여 묶었다.

서비스를 거의 변하지 않는 비즈니스 능력에 따라 구성하면 비교적 안정적인 아키텍처를 구축할 수 있다. 나중에 비즈니스 요건이 달라져도 아키텍처는 그대로 둔 채, 아키텍처를 구성하는 개별 컴포넌트는 발전시킬 수 있다.

서비스는 시간이 지남에 따라 변할 수 있다. 도메인 지식이 쌓일수록 서비스는 점점 정교해지기 때문이다. 그래서 아키텍처를 정의하는 과정에서는 각각 의 핵심 아키텍처 서비스와 나머지 서비스가 어떻게 협동하는지 살피는 과정이 중요하다. 가령 IPC가 너무 잦아 분해하는 것이 비효율적이라 서비스를 재결합 할 수 있다. 반대로 너무 복잡해서 서비스를 추후에 나눌 수도 있다.

2.2.4 서비스 정의: 하위 도메인 패턴별 분해

DDD는 객체 지향 도메인 모델 중심의 복잡한 소프트웨어 애플리케이션을 구축하는 방법이다. DDD에는 마이크로서비스 아키텍처에 적용하면 유용한 하위 도메인과 경계 컨텍스트 개념이 있다.

기존에는 전체 비즈니스를 포괄하는 단일 통합 모델을 만들었다. 이렇게 모델링하면 하나의 모델에 대해 조직 내 여러 부서의 합의를 이끌어 내기가 정말 어려운 단점이 있다. 가령 어떤 부서는 자기네가 필요한 것보다 너무 복잡한 모델이라고 불평할 수 있다. 더구나 부서마다 상이한 개념을 동일한 용어로(반대로 동일한 개념을 상이한 용어로) 표현하는 일도 비일비재한 탓에 도메인 모델이 외려 혼란을 일으킬 때도 있다. DDD는 범위가 분명한 도메인 모델을 여러 개 정의하여 기존 방식의 문제점을 해결하는 전혀 다른 방식의 모델링이다.

DDD는 도메인을 구성하는 각 하위 도메인마다 도메인 모델을 따로 정의한다. 하위 도메인은 비즈니스 능력과 같은 방법(비즈니스를 분석하고 상이한 전문 영역을 식별)으로 식별하므로 십중팔구 비즈니스 능력과 유사한 하위 도메인이 도출된다.

DDD의 하위 도메인, 경계 컨텍스트 개념은 마이크로서비스 아키텍처의 서비스와 잘 맞고, 마이크로서비스 아키텍처의 서비스 자율 팀 개념은 도메인 모델을 개별 팀이 소유/개발한다는 DDD 사고방식과 어울린다. 자체 도메인 모델을 가진 하위 도메인이라는 개념 덕분에 만능 클래스를 제거하고 서비스로 분해하기가 더 수월해진다.

2.2.5 분해지침

마이크로서비스 아키텍처를 정의할 때 객체 지향 설계에 근거한 두 가지 원칙이 있다. 클래스의 책임을 정의하는 단일 책임 원칙과 클래스를 패키지로 구성하는 공동 폐쇄 원칙이다.

  1. 단일 책임 원칙

단일 책임 원칙은 ‘클래스는 오직 하나의 변경 사유를 가져야 한다’로 표현할 수 있다. 이 원칙을 마이크로서비스 아키텍처에 적용하면 하나의 책임만 가진 작고 응집된 서비스를 정의할 수 있다.

  1. 공동 폐쇄 원칙

공동 폐쇄 원칙은 ‘패키지의 클래스들은 동일한 유형의 변경에 대해 닫혀 있어야 한다. 패키지에 영향을 주는 변경은 그 패키지에 속한 모든 클래스에 영향을 끼친다’로 표현할 수 있다. 즉, 어떤 두 클래스가 동일한 사유로 맞물려 변경되면 동일한 패키지에 있어야 한다는 것이다. 가령 동일한 비즈니스 규칙도 상이한 측면을 구현한 클래스가 여럿 있을 수 있다. 이 비즈니스 규칙이 나중에 바뀌어도 개발자는 가급적 소수의 패키지에 있는 코드만 고치면 될 수 있게 만들자는 것이다. CCP를 잘 지키면 유지보수성이 향상된다. CCP를 적용해 마이크로서비스 아키텍처를 구축하면 동일한 사유로 변경되는 컴포넌트를 모두 같은 서비스로 묶을 수 있다. 요건이 바뀌어도 수정/배포할 서비스 개수는 줄어든다. CCP는 분산 모놀리스 안티패턴의 해독제인 셈이다.

2.2.6 서비스 분해의 장애물

  1. 네트워크 지연
  2. 동기 통신으로 인한 가용성 저하
  3. 여러 서비스에 걸쳐 데이터 일관성 유지
  4. 데이터의 일관된 뷰 확보
  5. 분해를 저해하는 만능 클래스

2.2.7 서비스 API 정의

시스템 작업과 서비스 후보를 목록화한 후에는 각 서비스별 API(작업과 이벤트)를 정의해야 한다. 서비스 API 작업은 (1) 외부 클라이언트 또는 타 서비스가 호출하는 시스템 작업과 (2) 서비스 간 협동을 지원하기 위해 타 서비스 호출 전용으로 만든 작업, 둘 중 하나이다. 서비스 API를 정의하려면 우선 각각의 시스템 작업을 서비스로 매핑한 후, 그 시스템 작업을 구현하려면 어느 서비스가 서로 협동해야 할 지 파악해야 한다. 협동이 필요한 서비스가 있으면 어느 서비스가 어느 API를 타 서비스에 제공해야 할지도 정해야 한다.

1. 시스템 작업을 서비스로 배정

제일 먼저 어느 서비스가 요청의 진입점인지 결정해야 한다. 대부분의 시스템 작업은 자연스레 서비스로 매핑되지만, 간혹 매핑 관계가 분명하지 않을 때도 있다. 예를 들어, A라는 시스템 작업이 의미적으로는 B 서비스와 관련이 깊더라도, A 작업을 필요로 하는 주체가 C 서비스라면, A 작업을 C 서비스에 배정할 수도 있다. A 작업을 처리하는 데 필요한 정보를 갖고 있는 서비스에 배정하는 게 더 타당할 때도 있을 것이다.

2. 서비스 간 협동 지원에 필요한 API 확정

시스템 작업을 각 서비스에 배정한 후, 각 시스템 작업을 처리하기 위해 서비스가 어떻게 협동해야 할 지 정한다. 서비스 하나로 처리 가능한 작업도 있지만, 작업은 대부분 여러 서비스에 걸쳐 있다. 요청을 처리하는 데 필요한 데이터가 여러 서비스에 흩어져 있는 경우도 있다. 따라서 서비스 API를 온전하게 정의하려면 각 시스템 작업을 면밀히 분석 후 서로 어떻게 협력해야 할지 결정해야 한다.

서비스api정의