Design Patterns

마이크로 서비스 패턴 - 2장. 분해전략

FreeEnd 2022. 2. 13. 23:47
반응형

이 블로그는 마이크로서비스패턴 (길벗) 책 내용을 스터디를 위해 정리 한 내용 입니다. 

책구매는 바로가기 에서 구매 가능합니다.

 

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

 마이크로 서비스 아키텍처의 핵심 사상은 기능 분해이다. 간단히 애플리케이션을 여러 서비스로 구성 하는 것이다.

소프트웨어 아키텍쳐는 구성요소 및 그들간의 디펜던시 로 엮은 구조물이다.

어플리케이션 아키텍쳐는 다차원적이므로 기술하는 방법이 다양하다.

 

아키텍처가 주요한 이유는, 소프트웨어의 속성 지표가 아키텍처에 의해 결정되기 떄문이다. 

소프트웨어는 신뢰, 확정, 보안등이 목표였지만 지금은 신속/안전하게 소프트웨어를 전달 하는 능력도 중요해지고 있다.

 

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

 소프트웨어 아키텍처의 정의

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

라고 정의 된다.

 

 분해가 중요한 이유는

1. 업무와 지식을 분리 하여 전문지식을 보유한 사람들이 함께 생산적으로 어플리케이션을 개발 할 수 있음

2. 소프트웨어 엘리먼트가 어떻게 상호 작용하는지 밝힘.

 

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

논리뷰 : 개발자가 작성한 소프트웨어 엘리먼트, 클라스, 패키지가 해당된다. 상속, 연관 의존등 클래스 패키지간의 관계를 말한다.

 

구현뷰 : 빌드 시스템이 만들어낸것으로, 모듈(패키징코드) 과 패키지(배포단위)로 구성된다. 일반적으로 모듈은 JAR 이며,  컴포넌트는 실행가능한 JAR 혹은 WAR  이다.

 

프로세스뷰 : 컴포넌트가 실행된 생태로, 런타임 컴포넌트, 엘리먼트는 개별 프로세스이고, IPC 는 프로세스간의 통신을 나타낸다.

 

배포뷰 : 프로세스가 머신에 매핑되는 방법이다. 

 

시나리오 (+1) : 특정 뷰 내에서 얼마나 다양한 아키테처 요소가 협동하여 요청을 처리하는지를 기술

 

아키텍처의 중요성

1.  어플리케이션이 해야 할 일을 정의한 기능요건

 기능 요건은 아키텍처와 거의 무관하다.

 

2.  ~ 성으로 끝나는 서비스 품질 요건

 아키텍처는 품질 요건을 충족시킬수 있게 설계 해야 함으로써 가장 중요한 요소이다.

 

 

아키텍처 스타일 개요

"아키텍처 스타일은 치계적인 조직의 관점에서 시스템군을 정의한다. 좀더 구체적으로 말하면, 아키텍처 스타일은 그 스타일로 만든 인스턴스에서 사용 가능한 컴포넌트와 커넥터, 보케블러리, 그리고 이들을 조합할수 있는 제약 조건을 결정한다." <소프트웨어 아키텍처 개론)

 

어플리케이션은 대부분 아키텍처 스타일을 조합해서 사용한다.

모놀리식 아키텍처도 구현뷰를 하나의 컴포넌트로 구성한 아키텍처 스타일이다.

마이크로 서비스아키텍처는 어플리케이션을 여러 서비스로 구성해 느슨하게 결합한 아키텍처 스타일이다.

 

 

계층화 아키텍처 스타일

 계층마다 명확하게 정의된 역할을 분담함. 계층간 디펜던시는 아키텍처로 제한한다. 

 

 표현계층 (presentation Layer) : 사용자 인터페이스 또는 api 구현계층

 비즈니스 로직 계층 (Business Logic Layer) : 비지니스 로직이 구현된 계층

 영속화 계층 (Persistence Layer) : DB 상호작용이 구현된 계층

 

 계층화 아키텍처는 단점이 존재 하는데,

 표현계층, 영속회계층이 하나고, 비지니스 로직계층은 영속화 계층에 의존하므로, DB없이 테스트가 불가능 하다.

 

 

육각형 아키텍처 스타일

 논리뷰를 비지니스 로직중심으로 구성한 계층화 아키텍처스타일의 대안이다.

 

 비지니스로직을 호출하여 외부에서 들어온 요청을 처리하는 Inound Adaptor, 

 비지니스로직에 의해 호출되고 외부 어플리케이션을 호출하는  Outbound Adaptor 로 나눔으로써 비지니스 로직이 어댑터에 전혀의존하지 않는다는게 특징이다. 

 

 Adaptor 는 비지니스로직을 감싸고 있다. Inbound, Outbound 두 종류의 Adaptor 가 있다. 

외부에서 들어온 요청(Rest API, consumer, MVC 컨트롤러 등) 은 Inbound 를 이용해 처리하며, 동일한 Inbound port 는 여러 Adaptor 가 호출 할 수 도 있다.

 Outbound 는 비지니스 로직에 들어온 요청을 외부 어플리케이션, 혹은 서비스를 호출해 처리한다. 이는 DAO 가 될수도 있고, 원격 어플리케이션, 이벤트 등일 수도 있다.

 

 육각형 아키텍처의 장점은 비지니스 로직에 있던 표현/DAO 로직등이 어탭터와 분리되어 있기때문에 표현/DAO 로직 어디에도 의존하지 않는다는 장점이 있음. 이로인해 테스트가 쉬우며 아키텍처가 좀더 명확하게 나타낼 수 있다.

 

 

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

 모놀리식 아키텍쳐란, 구현 뷰를 단일 컴포넌트(WAR) 로 구성한 아키텍처 스타일이다. 

 마이크로서비스 아키텍처는 구현뷰를 다수의 컴포넌트로 나누어 구성한다. 컴포넌트는 개별 서비스이고 독립적으로 배포 가능한 서비스이다.

 

 서비스란 무엇인가?

 서비스는 단독 배포가 가능한 소프트웨어 컴포넌트이다.  클라이언트가 자신의 기능의 접근할 수 있도록 커맨트, 쿼리 , 이벤트로 구성된 API 를 제공한다. 이 서비스는 구현 상세가 캡슐화 되어 있어 API 를 통하지 않고 서비스에 접근 할수 없도록 모듈성이 보장된다. 

 API 는 비지니스로직과 소통하는 Adaptor 를 이용해 구현한다. 

 작업어댑터(Operation Adaptor) 는 비지니스 로직을 호출하여 요청된 내용을 처리하며, 이벤트어댑터(event Adaptor) 는 비지니스로직이 처리한 이벤트를 발행 한다.

 

 

느슨한 결합 (LOOSE COUPLING)

느슨한 결합(Loose coupling) 은 마이크로서비스의 주요 특성중 하나이다. 

각기 서비스는 API 를 통해서만 작업 (상호작용) 하므로 각 클라이언트에 영향을 주지 않는다. 이로인해 각 서비스별 배포, 테스트가 용이하여 유지보수성이 높아진다. 

 각 클라이언트는 DB를 직접 조회 하지 않기때문에 DB 스키마 변경으로인한 각 시스템별 조율이 필요 없고, Lock 등의 상황에서도 자유롭다.

 

공유 라이브러리의 역할

 일반적으로 공유 라이브러리가 필요한 경우가 많다. 각 개별 시스템에서 동일한 로직을 수행하는 코드가 필요할 경우 공통의 코드를 작성하고 라이브러리를 배포하여 동일한 로직을 수행하게 하는것이 리소스 비용을 낮출 수 있다. 하지만 해당 로직이 변경이 되야 하면 디펜던시를 조사해 영향범위를 산정하고 수정해야 하므로, 이로인해 시간적 비용이 높아지고, 수정에 대한 위험도가 높아 질 수 있다. 

 거의 수정될 일이 없는 로직은 라이브러리로 공통화 하여 배포 해도 상관 없지만, 그렇지 않은 부분은 분명 재고 해야 한다.

 

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

마이크로 서비스는 크기보다는 작은팀이 가장 짧은 시간에, 협동하는 부분은 최소로 하여 개발 가능한 서비스를 설꼐 해야한다.  내 서비스가 다른 서비스의 변경으로 인해 영향을 받는다면 느슨하게 결합 되어 있지 않다는 증거이다. 

 

 

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

모놀리식을 마이크로서비스 아키텍처로 정의 하는 방법

1 단계, 어플리케이션 요건을 핵심적 요청으로 추출. 어플리케이션이 처리하는 요청을 추상화함

   예를 들어 데이터를 업데이트 하거나, 조회하는 작업등.    

2 단계, 어떻게 여러 서비스로 분해할지 결정 

   비지니스별 로직을 분할? 혹은 DDD의 하위 도메인별 서비스 구성?

3 단계, 서비스별로 API 를 정의 

   1단계의 추상화된 요청을 인터페이스로 정의

 

 분해하는데 방해가 되는 요소

네트워크 지연, 동기 통신으로 인한 가용성 감소, 데이터 일관성유지, 도처에 숨어있는 어플리케이션의 만능 클래스

 

 

시스템작업식별

 어플리케이션 아키텍처를 정의 할때는 우선 시스템을 정의 해야 한다. 

1단계, 보케블러리를 제공하는 핵심 클래스로 구성된 고수준의 도메인 모델을 생성

2단계, 시스템 작업 식별후, 그 동작을 도메인 모델 관점에서 기술.

 

고수준의 도메인 모델 생성 (CREATING A HIGH-LEVEL DOMAIN MODEL)

 최종적으로 구현할 모델보다는 단순하게 대략적인 도메인 모델을 그려본다. 이 작업은 시스템 작업의 동작을 기술하는데 필요한 보케블러리를 정의하기 때문에 유용하다.

 간단히 시나리오를 작성해 대략적으로 정의한다.

 

Given a consumer
  And a restaurant
  And a delivery address/time that can be served by that restaurant
  And an order total that meets the restaurant's order minimum


When the consumer places an order for the restaurant


Then consumer's credit card is authorized
  And an order is created in the PENDING_ACCEPTANCE state
  And the order is associated with the consumer
  And the order is associated with the restaurant

 

작성된 시나리오를 기준으로 개별 확장한다.

 

Given an order that is in the PENDING_ACCEPTANCE state
   and a courier that is available to deliver the order


When a restaurant accepts an order with a promise to prepare by a particular time


Then the state of the order is changed to ACCEPTED
  And the order's promiseByTime is updated to the promised time
  And the courier is assigned to deliver the order

 

 

각기 식별된 시나리오를 확장하면 도메인 모델이 완성 된다.

 

시스템 작업 정의 (DEFINING SYSTEM OPERATIONS)

어플리케이션이 어떤 요청을 처리할 지 식별하는 단계이다.

 

 * 커맨드 (Command) : 데이터의 생성, 업데이트

 * 쿼리 (Queries)  : 데이터 조회

 

커맨드를 구분하려면, 우선 시나리오에 포함된 동사 ( 주문 생성, 주문접수, 주문픽업 등) 를 먼저 구분한다. 이때 매개변수, 반환값, 동작방식등의 명세를 기준으로 정의한다.

 

 

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

 비지니스의 능력 (업무범위) 에 따라 분해 해야 한다. 주문관리, 재고관리, 선적등 이다.

 

 비지니스 능력에 따라 분해해야 한다. (BUSINESS CAPABILITIES DEFINE WHAT AN ORGANIZATION DOES)

비지니스 능력은 조직이 어떤 일을 하는지 알 수 있다. 예전에는 은행에 직접가서 예금하였지만 지금은 ATM, 온라인, 폰, 스마트폰등 다양한 방법으로 예금한다. 예금이라는 업무는 불변 한다는 것이다.

 

비지니스 능력 식별

 비지니스 능력은 조직의목표, 구조, 비지니스 프로세스를 분석하여 식별. 

 비지니스 능력은 여러개의 하위 능력으로 분해 될수 있다. 예를 들면, 크레임 관리라는 능력은 클레임정보관리, 클레임 검토, 클레임지불관리등이다.

 

 

비지니스 능력을 여러 서비스로

 이렇게 식별된 비지니스 능력은 연관된 그룹으로 나눈다. 

 거의 변하지 않는 능력을 기준으로 서비스를 구성하면 비교적 안정적으로 아키텍처를 구축할 수 있다. 나중에 비지니스 요건이 달라져도 개별 컴포너트는 거의 변하지 않고 세부 비지니스만 발전 할 수 있다.

 

 

 

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

 도메인 내부에서 문제 해결이 가능한 형태로 도메인을 모델링 하는 기법.

DDD는 도메인을 구성하는 각 하위 도메인마다 도메인 모델을 따로 정의한다. 

분해지침

단일 책임 원칙 (SINGLE RESPONSIBILITY PRINCIPLE)

 "클래스는 오직 하나의 변경 사유를 가져야 한다."

 클래스는 오직 하나의 책임만 가져야 한다. 

 

공동 폐쇄 원칙 (COMMON CLOSURE PRINCIPLE)

 "패키지의 클래스들은 동잃란 유형의 변경에 대해 닫혀 있어야 한다. 패키지에 영향을 주는 변경은 그 패키지에 속한 모든 클래스에 영향을 끼친다." 

두 클래스가 동일한 사유로 맞물려서 변경되면 동일한 패키지 내에 존재 해야 한다.

 

서비스 분해의 장애물

네트워크 지연

 서비스가 여러개로 나뉘게 되면 각 서비스간에 전송되는 데이터 횟수가 증가하게 되어 지연이 발생할 수 있다.  이는 여러 데이터를 한번에 가져오거나, 함수호출로 대체 하는 등으로 지연 시간을 줄일 수 있다.

 

동기 IPC 로 인한 가용성 저하

여러개의 서비스중 하나라도 장애 일 경우, 전체적인 요청이 처리 되지 않을 수 있다. 이는 비동기 메시징으로 가용성을 높일 수 있다.

 

여러 서비스에 걸친 데이터 일관성 유지

 서비스별로 데이터가 나누어져 있기때문에 하나의 트랜잭션에서 여러개의 데이터베이스에 업데이트를 수행해야 해야 한다. 이때 업데이트는 원자성을 보장해야 한다.

 이는 2 Phase Commit (2PC) 이나 SAGA 등으로 처리 해야 한다. 

 최종적으로 일관성을 유지 해야 하는데, 이는 분해의 걸림돌중 하나이다.

 

일관된 데이터 뷰 확보

모놀리틱 서비스는 하나의 어플리케이션에서 ACID(원자성, 일관성, 고립성, 지속성) 을 보장하기 때문에 일관된 데이터 뷰가 제공되지만, 마이크로서비스 아키텍처는 데이터 베이스가 나누어져 있기 때문에 전역 범위에서 제공하기는 힘들다. 

 

만능 클래스는 분해의 걸림돌

 대부분의 서비스들은 모든 속성(컬럼)을 가지는 단일 테이블들을 갖고 있는 경우가 많다. 이는 비지니스 로직을 서비스로 분리하는데 걸림돌이 된다. 

 이경우 DDD 를 적용해 자체 도메인 모델을 갖고 있는 개별 하위 도메인으로 나누어 해결한다. 

 

 

서비스 API 정의

 시스템작업과 서비스 후보를 목록화 했으면, API (작업, 이벤트) 를 정의 해야 한다.,

서비스 작업은 외부 요청에 대한 처리와 서비스간의 처리를 위해 타 서비스 호출용으로 만든 작업 이다.

서비스 이벤트는 주로 다른 서비스와의 협업을 위해 발행한다. 예를 들어 SAGA 를 구현하기위해, 이벤트를 이용해 CQRS 뷰를 업데이트 하기 위해 사용된다. 

 

 

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

 서비스 진입점이 어디인지 정의 해야 한다.  대부분의 시스템 작업은 서비스로 매핑 될수 있으나 아닐 수도 있다. 

예를들어 배달원의 위치를 업데이트 하는 noteUpdatedLocation() 은 배달원과 관련있기 떄문에 배달원 서비스에 있어야 할것으로 보이나, 달리 생각해보면 배달원의 위치가 필요한 주체는 배달 서비스 이기때문이다.

 어떤 작업이 제공하는 정보가 필요한 서비스에 대정하는게 더 합리적이다.

 

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

 작업 대부분은 여러 서비스에 나뉘어져 있으며, 요청을 처리하는데 여러 서비스를 각각 호출 해야 한다. 이렇게 분리되어 있는 각각의 시스템 작업을 면밀히 분석해 어떻게 협동해야 할지 결정해야 한다.

 

참고 : https://learn.co/lessons/microservices-patterns-chapter-1-2

 

 

반응형