intro

2018년 2월에 한번 읽었던 마이크로서비스 아키텍처 구축, 처음에는 끝까지 읽음을 목표로 했고, 두번째 읽을 때는 70%의 이해도를 목표로 읽는다.

대용량 시스템의 효율적인 분산 설계 기법

1장 마이크로서비스 : 마이크로서비스가 가져다주는 주요 혜택을 몇몇 단점과 함께 소개한다. 2장진화적 아키텍트 : 아키텍트로서 상충관계의 타협점을 찾는 과정에서 직면하는 어려움을 논의하고 마이크로서비스에 대해 생각해야하는 많은 사항을 구체적 다룬다. 3장 서비스 모델링하기 : 도메인 주도 설계 기술을 사용해서 마이크로서비스의 경계를 정의하기 시작한다. 4장 통합 : 어떤 서비스 협업 기술이 가장 도움이 될지 논의하며 특정 기술의 구현체보다 깊이 살펴본다. 사용자 인터페이스에 대한 주제를 탐구하며 레거시 제품과 상용제품을 통합하는 주제도 살펴본다. 5장 모놀리스 분해하기 : 대규모이며 변경이 어려운 모놀리식 시스템의 해독제로 마이크로서비스에 대해 주목한다. 이것을 이장에서 다루는 주제다 6장 배포 : 배포에 대한 탐구 7장 테스팅 : 별개의 여러 서비스를 배포할 때 우려가 되는 테스팅의 주제에 대해 깊이 살펴본다. 주목해야 할 것은 소프트웨어품질 보장에 도움을 주는 소비자 주도 계약의 역할이다. 8장 모니터링 : 세분화된 시스템을 모니터링 하고 분산 시스템에서 출현하는 보잡성을 다루는 방법을 살펴본다. 9장 보안 : 사용자 대 서비스, 서비스 대 서비스 인증 및 권한 처리 방법을 고민. 10장 콘웨이 법칙과 시스템 설계 : 조직 구조와 아키텍처의 상호작용에 중점을 둔다. 11장 대규모 마이크로 서비스: 대규모로 운영하기 위한 검토를 시작한다. 12장종합 정리 : 마이크로서비스를 다른 것과 차별화하는 핵심 요소를 알아본다. 마이크로서비스 7원칙과 이책에서 설명한 핵심 내용

Chapter 1 마이크로서비스 아키텍처 구축

도메인 주도설계 : 실세계를 코드로 표현하는 일이 얼마나 중요한지 이해하는데 도움, 시스템을 모델링하는 더 나은 방법을 보여줌 지속적 배포 : 체크인한 모든 것을 릴리스 후보로 대해야 한드는 생각을 우리에게 주입시키고, 어떠헥 하면 더 효율적으로 소프트웨어를 양산할 수 있는지 보여준다. 웹 동작 원리를 이해함으로 : 머신들이 통신할 수 있는 더 나은 방법을 개발 앨리스테어 콕법의 육각형 아키텍쳐 개념 : 우리가 비지니스 로직이 숨을 수 있는 계층형 아키텍처로부터 멀어지도록 안내했다. 가상화 플랫폼은: 머신을 원하는 대로 프로비저닝하고 규모를 조절하게 해주었다. 인프라 스트럭처 자동화는 : 머신을 확장하는 방법을 제공한다.

많은 조직은 세분화된 마이크로서비스 아키텍터를 포용하는 과정에서 소프트웨어를 더 빨리 배포하고 새로운 기술도 흡수 할 수 있다는 것을 발견했다

1.1 마이크로서비스란

작고 자율적으로 협업하는 서비스를 의미한다.

1.1.1 작고, 한가지 일을 잘하는데 주력

얼마나 작아야 작은것 인가?! => 마이크로 서비스란 2주 안에 재작성될 수 있는 것 (호주의 부동산 매매정보 사이트의 존이브스) => 충분히 작아서 더이상 작아질 수 없는 크기 (샘 뉴먼)

1.1.2 자율성

마이크로 서비스는 분리된 개체다. 서비스간의 분산도를 높이고 서비스간에 밀접하게 연결되어 생기는 문제점을 줄이기 위해 서비스 사이의 모든 통신은 네트워크 호출을 통해 이루어진다. 이 서비스 들은 독집적으로 변경될 수 있고, 다른 서비스의 변경없이 독립적으로 배포될 수 있어야 한다. 이때 우리는 무엇을 외부에 드러내고 무엇을 내부에 두어야하는지 고민해야 한다. 너무 많이 드러내면 소비자 서비스가 내부구현과 엮이게 된다. 이경우 서비스를 변경할 때소비자와 조율할 께 많아지므로 자율성이 줄어든다.

1.2 주요혜택

1.2.1 기술 이기종성

다수의 협업 서비스로 구성된 시스템에서는 각 서비스가 다른 기술을 사용하도록 결정할 수 있다. 물론 여러 기술을 함께 사용하면 문제가 전혀 없을 수 없다. 몇몇기업은 프로그래밍 언어 선택에 일부러 제한을 걸어둔다.

1.2.2 회복성

회복 공학의 핵심 개념은 '격벽'이다. 즉 한 시스템의 컴포넌트에 장애가 발생하더라도 그 장애가 전파되지 않는다면 해당 문제를 격리하고 나머지 시스템을 계속 작동 시킬수 있다.

모놀리식 시스템은 장애 가능성을 낮추기 위해 수만은 머신 상에서 실행되어야 하지만 마이크로 서비스에서는 서비스의 전체 장애를 차단하고 기능을 적절히 저하 시키는 시스템을 구축할 수 있다.

하지만 주의할 점도 있다. 마이크로서비스 시스템이 향상된 회복력을 적절히 수용할 수 있도록 분산 시스템이 마주한 새로운 장애 요인들을 이해해야 한다.

1.2.3 확장성

만약 전체 시스템에서 작은 한 부분만 성능이 떨어지는데, 모놀리식에 묶여 있다면 전체를 한덩어리처럼 확장해야한다. 그러나 작은 서비스들로 구성되어 있다면 필요한 서비스만 확장할 수 있다.

1.2.4 배포용이성

마이크로 서비스를 이용하면 하나의 서비스만 변경할 수 있고, 나머지 시스템과 독립적으로 배포할 수 있다.

1.2.5 조직 부합성

대부분 대규모 팀과 코드베이스 때문에 생긴 문제를 경험한 바 있을 것이다. 팀의 분산이 있으면 더 심하다. 더 작은 팀이 더 작은 코드베이스로 일할 때 더 생산적임을 알고 있다.

1.2.6 조합성

분산 시스템과 서비스 지향 아키텍처의 주요 장점 중 하나는 기능을 재사용할 기회가 많다는 것이다. 마이크로서비스를 통해 다양한 방법과 목적으로 우리가 제공하는 기능이 소비되도록 할 수 있다.

1.2.7 대체 가능성을 위한 최적화

마이크로서비스 방식을 사용하는 팀은 필요할 경우 서비스를 완전히 재작정하는 것을 편하게 생각하며, 서비스가 더이상 필요치 않으면 쉽게 제거할 수 있다. 코드베이스가 단지 몇백줄 규모일 때 개발자들은 자기가 쓴 코드이더라도 집작하지 않으며 교체비용 또한 적다.

1.3 서비스 지향 아키텍처란

서비스 지향 아키텍터(Service-Oriented Architecture)란 서비스의 최종 능력집합을 제공하는 여러 서비스가 서로 협업하도록 하는 설계 접근 방식이다. 여기서 서비스란 일반적으로 완전히 분리된 운영시스템의 프로세스를 의미한다. 서비스 간 통신은 네트워크 호출로 이루어진다.

대규모 모놀리식 애플리케이션의 도전에 대한 대응으로 출현한 SOA 는 소프트웨어의 재사용성 장려를 목표로한다.

1.4 기타 분해기술

마이크로서비스 기반 아키텍처의 수많은 이점은 마이크로서비스가 가진 세분화 특성과 문제 해결을 위한 더 많은 선택의 기회를 제공하는 데서 비롯된다.

1.4.1 공유 라이브러리

라이브러리는 팀과 서비스 간에 긴응을 공유하도록 한다. 예를 들어 유용한 유틸리티 모음을 만들 수 잇고 재사용 가능한 정적 라이브러리를 만들수도 있다. 이러한 라이브러리를 중심으로 팀이 구성될 수 있고 라이브러리를 재사용할 수 있다. 하지만 단점도 존재한다. 첫째, 진정한 기술 이기종성을 읽게된다. 둘째, 시스템 일부를 독립적으로 확장하기 어려워 진다. 셋째, 동적 라이브러리를 사용하지 않는한 전체 프로세스를 재배포하지 않고서는 새로운 라이브러리를 배포할 수 없으므로 변경부분만 격리하여 배포할 수 있는 능력이 떨어진다. 넷째, 시스템 화복력을 보장하는 구조적 안전 조치를 취할 명확한 접합구가 부족하다.

1.4.2 모듈

일부 언어는 단순한 라이브러리보다 뛰어난 독자적인 모듈 분해 기술을 제공한다. 이러한 언어는 모듀르이 수명주기 관리 기능을 일부 제공한다. 예를 들어 프로세스를 종료하지 않고도 실행중인 프로세스에 변경된 모듈을 배포할 수 있다.

1.5 은총알은 없다.

마이크로서비스가 공자도 은총알(복잡한 문제의 단순하고 신속한 해결책을 의미)도 아니며, 황금망치로 오인되는 일이 없어야 한다고 강조한다. 마이크로 서비스는 분산 시스템과 연관된 모든 종류의 복잡성을 내포하고 있다.

Chapter 2 진화적 아키텍트

이 장에서는 아키텍트의 역할이 무엇인지에 대한 상당히 완고한 견해를 유지하면서, 바라건대 이론가들에게 주먹을 날리고자 한다.

2.1 부정확한 비교

당신은 계속 같은 말을 하고 있지마, 그것이 당신이 생각하는 의미는 아닌 것 같아요 _영화<프린세스 브라이드=""> 중에서

우리를 엔지니어 또는 건축가와 비교한다면 다른 사람들에게 폐를 끼칠 위험이 있다. 그러나 불행하게도 아직은 아키텍트라는 단어에서 벗어날 수 없으므로 할 수 있는 최선의 방안은 우리 입장에서 아키텍트의 의미를 재정의하는 것이다.

2.2 아키텍트에 대한 진화적 관점

에릭 도넌버그는 아키텍트의 역할을 건축가보다는 도시설계자에 가가운 의미로 접근해야 한다는 아이디어를 냈다.

2.3 구역화

2.4 원칙적인 접근법

규칙은 바보에게는 복종이지만 현자에게는 지침이다 _더글라스 베이더

시스템 설계상의 결정은 모두 트레이드오프와 관련한다.

의사결정을 프레이밍할 수 있는 가장 좋은 방법은 성취해야 할 목표에 기반하여 일련의 원칙과 실천사항을 정의하는 것이다.

2.4.1 전략적 목표

전략적 목표에서는 회사가 어디를 향해 나아가고 있는지, 그리고 고객을 행복하게 하기 위해 어떻게 해야할지 언급해야 한다. 이때 핵심은 전략적 목표가 조직이 지향하는 바와 일치해야 한다는 것이다.

2.4.2 원칙

원칙은 더 큰 목표를 위해 해야 할 일을 정렬하는 규칙으로, 때로는 변경될 수 있다. 원칙의 수는 10개 미만인 것이 좋다. 이는 사람들이 쉽게 기어갈 수 있고 작은 벽보에 붙을 수 있는 개수다.

2.4.3 실천사항

실청 사항은 원칙을 실행하는 방법으로, 업무 수행을 위한 자세하ㅗ도 실질적인 지침이다. 이것은 대개 기술 명세적이며, 어떤 개발자든 이해할 수 있도록 충분히 구체적이여야 한다.

원칙과 마찬가지로 실천사항은 때때로 조직의 제약을 반영한다. 실천사항은 아키텍트의 원칙을 뒷받침해야 한다.

2.4.4 원칙과 실천사항의 결합

누군가의 원칙이 다른 사람에게는 실천 사항이 될 수 있다.

2.4.5 실제 사례

2.5 필수 기준

실천 사항에 따라 작업하고, 결정할 트레이드오프에 대해 고민할 때 찾아내야 할 핵심 균형 중 하나는 여러분 시스템이 얼마나 변동성을 허용할 수 있는가 하는 점이다. 서비스와 서비스간에 어떤 부분이 일정해야 하는지 식ㅂㄹ하는 비결 중 하나는 바람직하게 동작하는 서비스의 모습을 정의하는 것이다.

우리가 큰들에서 시스템을 생각한다면 '자율적인 수명주기를 가지면서도 함께 협업하는 수많은 작은 부품으로 구성된 응집력 있는 시스템이 되어야 한다' 거시적 시작을 읽지 않으면서도 개별 마이크로서비스 간의 자율성 최적화와 관련된 균형을 찾아야 한다.

2.5.1 모니터링

우리는 서비스 간 경계를 넘어 시스템 상태를 일관되게 살펴볼 수 있어야 한다. 시스템 전체의 상태를 볼수 있어야한다.

2.5.2 인터페이스ㅅ

서비스 간 인터페이스 기술의 개수는 가능한 최소로 유지하는 편이 사로운 소비자를 통합하는데 도움이 된다.

2.5.3 아키텍처 안정성

아키텍트는 오동작하는 하나의 서비스가 전체를 망가뜨리게 해서는 안되며, 서비스들이 비정상적인 하위 호출로부터 자신을 잘 보호하도록 해야한다.

2.6 코드를 통한 통제

함께 모여앉아 일을 수행하는 방식을 합의하는 것은 좋은 아이디어지만, 사람들에게 이러한 지침을 따르게 하는 것은 그다지 유쾌한 일이 아니다.

2.6.1 본보기

문서로 기록하는 것은 바람직하며 유용하다. 그러나 개발자들은 그들이 직접실행하고 담험할 수 있는 코드를 선호한다.

2.6.2 맞춤형 서비스 템플릿

모든 개발자가 따를 수 있는 지침의 대부분을 약간의 노력만으로 손쉽게 만들 수 있다면 얼마나 멋질까? 만약 개발자가 각 서비스에 필요한 핵심 속성들을 구현할 준비가 된 코드를 가지고 있으면 어떻까? 드롭위자드 와 카욘 은 JVM 기반 오픈소스마이크로컨테이너다.

2.7 기술 부채

트레이드오프의 이해를 돕는 개념이 바로 기술 부채다. 기술부채가 누적되면 현실 세계의 부채처럼 지속적인 비용이 발생하며 우리가 지불해야 하는 어떤 것이 된다.

2.8 예외처리

충분히 많은 예외가 발견 된다면 결과적으로 현실을 재인식시킬 수 있도록 원칙과 실천사항을 변경하는 것이 타탕할 수 있다.

2.9 중앙에서의 거버넌스와 지희

거버넌스 : 기업의 목적이 이해관계자의 요구, 조건, 선택을 평가함으로써 달성될 수 있음을 보장한다. 우선순위 및 의사 결정을 통해 방향을 설정하고, 합의된 방향과 목표에 대한 성과, 준수, 과정을 모니터링한다. ( _COBIT 5 )

아키텍트의 담당 업무 중 하나는 거버넌스다.(여기는 기술 거버넌스 측면에 집중하려 한다)

아이들에게 자전거 타는 법을 가르칠때, 넘어질 것으로 보일때 마다 개입한다면 그들은 절대 자전거 타는 방법을 배울 수 없다. 다만 아이들이 차도나 오리 연못 근처로 방향을 트는 순간에는 개입해야 한다.

2.10 팀 만들기

훌륭한 소프트웨어는 훌륭한 사람에 의해 만들어진다.

2.11 마치며

진화적 아키택트의 핵심 책무

  1. 비전: 명료하게 소통되고, 시스템이 고객과 조직의 요구사항을 충족하도록 돕는 기술 비전이 있는지 확인하라.
  2. 공감: 고객과 동료에 대한 여러분 결정의 파급력을 이해하라
  3. 협업: 비전을 정의하고, 다듬고, 실행하기 위해 가능한 많은 동료와 협업하라
  4. 적응성: 기술 비전이 고객과 조직이 요구하는 것을 반영하는지 확인하라
  5. 자율성: 여러분 팀의 표준화와 자율성 사이에서 올바른 균형을 찾아라
  6. 거버넌스: 시스템이 기술 비전에 맞게 구현되고 있는지 확인하라.

Chapter 3 서비스 모델링하기

나와 반대 논리를 편친 이교도가 "이 세상을 떠받치고 있는 존재가 누구인가?" 라는 질문에 대답한 것이 생각난다. 그는대답했다. "세상은 큰 거북이가 떠받치고 있다네. 하지만 그 거북이를 또 다른 거북이가 받치고 있지." _조셉바커(1854)

3.1 뮤직코퍼레이션 소개

이책 전반에 걸쳐 가상의 도메인에 대해 다룰 것이고, 마이크로서비스의 개념이 어떻게 현실 세계에서 작용하는지 살펴볼 것이다. 최첨단 온라인 소매 회사인 뮤직코퍼레이션! 최근까지도 오프라인 소매점 이었으나 축음기 음반 사업이 바닥을 친 후 온라인 사업에 집중

3.2 무엇이 좋은 서비스를 만드는가?

만약 실패한 SOA를 구현하고도 살아남아 있다면 운과 별개로 두가지 주요개념에 집중해야한다. 느슨한 결합과 강한 응집력이다. 이 두 개념이 잘못되면 나머지도 의미가 없다.

3.2.1 느슨한 결합

서비스가 서로 느슨히 결합되어 있으면 하나의 서비스가 변경될 때 다른 서비스가 변경되는 일이 없다.

그렇다면 강한 결합을 일으키는 요인은 무엇일까?! 전형적인 실수는 서비스를 서로 강하게 엮어 버리는 통합방식을 선택하는 것이다.

3.2.2 강한 응집력

우리는 서로 연관된 행의는 한데 모아두고, 그렇지 않은 것은 다른곳에 두고 싶어한다. 특정행위를 변경하고자 할때는 한 곳에서 변경하고 가능한 한 신속하게 릴리스할 수 있기를 원하기 때문에

3.3 경계가 있는 콘텍스트

도메인 주도 설계는 실제 세상의 도메인을 모델링하는 시스템 구축방법에 중점을 두고 있다. 모든 도메인은 다수의 경계가 있는 콘텍스트로 구성되며, 각 콘텍스트 내에는 외부와 통신할 필요가 없는 것뿐만아니라 경계가 있는 다른 콘덱스트 외부와 공유되는 모델이 함께 존재한다. 경계가 있는 모든 콘텍스트에는 명백한 인터페이스가 존재하며, 그것은 어떤 모델이 다른 콘텍스트와 공유될지 결정한다.

명료한 경계에 의한 강제된 구체적인 책임. 만약 경계가 있는 콘텍스트로부터 정보를 원하거나 콘텍스트 내부의 기능을 요청하고자한다면 모델을 이용하여 명료한 콘텍스트 경계와 의사소통해야 한다. "세포가 존재할 수 있는 이유는 세포막이 세포 내부와 외부에 있는 것을 구분하고, 어떤 것을 통과시킬지 결정ㅇ하기 때문이다."

뮤직코퍼레이션 사업 : 도메인은 운영중인 전체 사업에 해당한다. 창고부터 접수처까지, 재무부터 주문까지 모든것이 포함된다. 에반스가 언근합 경계가 있는 콘텍스트로 보이는 도메인 부분들을 생각해보자 뮤직코퍼레이션에서 창고는 배송될 주문을 고나리하고, 새로운 재고를 이눗하고, 지게차들이 바삐 움직인다. 다른 곳인 재무부서는 여전히 조직에서 매우 중요한 기능을 담당한다.

3.3.1 감춰진 공유 모델

뮤직코퍼레이션 의 경우, 재무부서와 창고를 서로 분리하는 경계가 있는 콘텍스트로 생각할 수 있다. 두 곳 모두 외부세상에 명시적인 인터페이스를 제공하며, 각자만 알고 있어야 할 세부사항이 있다. 재무부서 직원들은 회사평가를 산출하기 위해 우리가가진 재고 품목 정보가 필요하며, 따라서 재고 품목은 두 콘텍스트 간의 공유 모델이 된다. 그러나 창고 콘텍스트에 있는 재고품목의 모든것을 노출할 필요는 없다.

3.3.2 모듈과 서비스

우리는 어떤 모델을 공유해야 하는지, 어떤 내부 표현을 공유하면 안되는지 명확히 고려함으로써 강한 결함을 초래하는 잠재적함정 중 하나를 회피한다. 서로 조화를 이루는 비지니스 역량들이 존재하는 도메인 내부의 경계를 식병하며 우리가 바라는 높은 응집력을 제공한다.

관련된 코드를 한데 모으고 시스템 내의 다른 모듈과 결합도를 낮추기 위해 프로세스 경계 내에서는 모듈을 사용할 수 있다.

만약 서비스 경계가 도메인 경계에 있는 콘텍스트와 정렬되고, 마이크로서비스가 경계가 있는 콘텍스트를 잘 대표한다면, 우리는 마이크로서비스가 느슨히 결합되고 강하게 응집되도록 보장한다는 점에서 아주 좋은 출발을 하고 있는 것이다.

3.3.3 성급한 분해

시스템을 마이크로서비스로 성급하게 분리하면 막대한 비용이 소요될 수 있다. 특히 도메인에 대한 경험이 없다면 더더욱 그렇다. 여러모로 기존 코드베이스를 마이크로서비스로 분해하는 것이 처음부터 마이크로서비스로 가는 것보다 훨씬 쉽다.

3.4 비지니스 능력

조직 내에 존재하는 경계가 있는 콘텍스트에 관해 고민할 때는 공유 데이터 관점이 아닌 나머지 도메인을 제공하는 콘텍스트의 능력관점에서봐야한다. 예를 들어 창고는 현재 재고 리스트를 제공하고, 재무 콘텍스트는 월말 결산을 제공하거나 신규 채용에 대한 급여를 정할 수 있다. 이들 능력은 정보의 교환이 이루어지는 공유 모델을 필요로 할 수 있으나, 데이터에 대한 생각이 빈약한 CRUD 기반의 서비스를 초래하는 것을 필자는 자주 목격했다. 따라서 '이 콘텍스트는 무엇을 하는가?' 와 '그 일을 하기 위해 어떤 데이터가 필요한가?' 를 먼저 물어라

이들 능력이 서비스로 모델링될 때 그것은 네트워크를 통해 다른 협업자에 노출될 주요 행위가 된다.

3.5 거북이 밑에 거북이

아마 처음에 몇몇 큰단위의 제한된 콘텍스트를 보았을 거이다. 이들 경계가 있는 콘텍스트들은 더 많은 '경계가 있는 콘텍스트'들을 내포할 수 있다.

예를 들어 창고를 주문 조달과 재고 관리 또는 제품인수와 연관된 기능으로 분해할 수 있다.

마이크로서비스의 경계를 고려할 때는 우선 더 넓고 큰 단위의 콘텍스트 관점에서 생각한 뒤 접합부의 분리를 통한 혜택을 발견했을 때 내포된 콘텍스트에 따라 세분화하라.

일반적으로 가장 적합한 접근 방식에 대한 고정불변의 법칙은 없다. 그렇지만 완전분리 방식 대신 내포된 방식을 선택하는 것은 조직구조를 기반해야한다.

만약 주문조달, 재고관리, 제품인수가 각기 다른 팀에 의해 관리된다면 그들은 최상의 계층의 마이크로서비스가 될 자격이 있다. 반면 한 팀에 의해 관리된다면 내포 모델이 더 함리적일 것이다.

내포 방식을 선호하는 또 다른 이유로는 테스팅의 단순화를 위해 아키텍처를 큰 덩어리로 묶을 수 있다는 점을 둘수 있다.

3.6 비지니스 콘센트 관점에서의 커뮤니케이션

우리가 시스템에 구현하는 변경 사항은 종종 비지니스가 시스템의 동작방식을 변경하려는 경우며 우리는 고객에 노출되는 기능을 변경하고 있다. 만약 도메인을 대표하는 경계가 있는 콘텍스트들에 따라 우리 시스템이 분해된다면, 그 변경사항은 단일 마이크로서비스 경계로 격리되기 쉽다. 이렇게 되면 변경대상을 줄이고 신속한 배포가 가능하다.

3.7 기술적 경계

서비스가 부정확하게 모델링 되었을 때 어떻게 잘못되는지 지켜보는 것은 유용하다.

프론트엔드 애플리케이션은 무상태 공개 웹사이트로 구현되었고, 백엔드 시스템은 데이터 저장소에 대한 단순한 원격프로시저호출(RPC) 인터페이스 였다.

두 서비스 모두 자주 변경해야했고, 저수준의 RPC 스타일의 메서드 호출로 통신하므로 지나치게 부서지기 쉬었다. 서비스 인터페이스 역시 호출이 너무 많아 성능 문제를 야기했다. 결국 정교한 RPC 일괄처리 메커니즘이 플요했다. 필자는 많은 계층을 가졌을 뿐만 아니라 잘라내야 할 때 눈물 나게 했기 때문에 이것을 양파 아키텍터라고 불렀다.

추후 더 자세히 다룬다.

3.8 마치며

무엇이 좋은 서비스를 만드는지, 그리고 우리는 문제영역에서 느슨한 결합과 높은 응집력의 이중혜택을 가져다 주는 접합부를 어떻게 찾는지 배웠다.

경계있는 콘텍스트는 접합부를 찾는데 중요한 도구고, 마이크로서비스를 이 경계에 정렬하여 우리의 최종시스템이 온전한 장점들을 유지하도록 만들어야 한다.

여기서소개한 뮤직코퍼레이션 의 사례를 이책 전반에 걸쳐 사용할 것이다.

Chapter 4. 통합

통합에 대한 올바른 이해는 마이크로서비스 관련 기술에서 가장 중요한 요소다. 제대로 통합하면 마이크로서비스는 자율성을 유지하면서 다른것과 독립적으로 변경하거나 배포할 수 있지만 잘못 통합하면 재앙이 기다릴 것이다.

propose : SOA 에서 다른 시도들을 괴롭혀 왔으며, 마이크로 서비스를 향한 여정 곳곳에도 도사리고 있는 매우 위험한 몇가지 함정을 피하는 방법을 배울것이다.

4.1 이상적인 통합 기술 모색

하나의 마이크로서비스가 다른 마이크로서비스 와 통신하는 방법은 놀랄만큼 다양하다. 잠시후 살펴보고 우리가 선택하는 기술로부터 바라는것이 무엇인지 먼저 생각해보자.

4.1.1 호환성을 깨뜨리는 변경 피하기

개발자에 의한 변경은 때로 서비스 소비자 측의 변경까지 초래하는 결과를 가져온다. 가능하면 이러한 변경을 일으키지 않는 기술을 선택하고 싶을 것이다.

4.1.2 기술 중립적인 API 생성

필자가 마이크로서비스의 열혈 팬인 이유는 선택의 여지를 두는 것이 아주 좋아하기 때문이다. 또한 마이크로서비스 간의 통신에 사용되는 API가 특정기술에 종속되지 않도록 기술 중립성을 유지하는 것을 매우 중요하게 생각하는 이유이기도 하다.

4.1.3 소비자를 위한 서비스 단순화

우리는 소비자가 서비스를 쉽게 사용하기 원한다. 이상적으로 클라이언트에 기술 선택에 대한 완전한 자유를 주고 싶지만 실제로는 클라이언트 라이브러리를 제공하는 것이 손쉬운 대안일 것이다. 다만 그런 라이브러리들을 우리가 성취하려는 목표와 부합되지 않을 때가 많다.

4.1.4 내부 구현 상세 감추기

우리는소비자가 우리의 내부 구현에 종속되길 원하지 않는다. 내부에 종속되면 결국 결합도가 높아지고 마이크로서비스 내부를 변경하려 할때도 소비자에게 변경까지 요구하며 호환성을 깨뜨릴 수 있다는 것을 의미한다.

4.2 고객과의 인터페이싱

서비스 통합에 필요한 좋은 기술을 선택하는 데 도움이 되는 몇가지 지침이 있다.

사고 과정에 도움이 되도록 뮤직코퍼레이션의 실제사례를 예로 들어볼 것이다.

언뜻보기에 고객생성은 단순한 CRUD 명령의 집합으로 간주될 수 있지만 대부분 그렇게 단순하지 않다.

이같은 사실을 염두해 두고 뮤직코퍼레이션 시스템에서 고객과 협업할 수 있는 몇가지 방법을 살펴보자.

4.3 공유 데이터베이스

협업에서 필자와 동료들이 목격한 가장 일반적인 통합형테는 DB 통합니다. 충분히 일반적인 패턴이지만, 다음과 같은 어려움이 따른다.

  1. 외부에서 내부의 구현 상세를 조회하고 결합하는 것을 허용한다.
  2. 소비자들이 특정기술을 선택하는 범위가 제한된다.
  3. 행동양식에 대해 잠시 생각해보자.

데이터베이스 통합에서는 강한 응집력과 느슨한 결합력을 모두 잃는다.

4.4 동기와 비동기

다양한 기술 선택지의 각 세부내용을 살펴보기 전에 서비스 협업 방식 측면에서 우리가 내릴수 있는 가장 중요한 결정사항 중 하나인 동기와 비동기 방식을 논의해야 한다.

동기 통신은 언제 작업이 성공적으로 완료되었는지 알 수 있어서 추론하기 쉽다. 반면 비동기 통신은 클라이언트와 서버간에 오랫동안 접속을 유지하기 어려운 장기 작업에 유용하다. 또한 결과를 기다리는 동안 중단된 호출이 성능 저하를 일으키는 곳에서 짧은 지연시간이 필요할 때 적합하다.

4.5 오케스트레이션 과 코레오크래피

점점 더 복잡한 로직을 모델링하기 전반적으로 시작하면서 우리는 각 서비스의 경계를 넘나드는 비지니스 프로세스들을 관리하는 문제를 다루어야한다. 그럼 뮤직코퍼레이션 사례에서 고객을 생성할 때 어떤 일이 발생하는지 살펴보자

  1. 고객에 대한 '적립포인트 은행'에 새로운 레코드가 생성된다.
  2. 자체 우편 시스템이 가입 환영 패키지를 보낸다.
  3. 고객에게 가입환영 이메일을 보낸다.

이 흐름을 실제로 구현하는 두가지 아키텍처 방식이 있다. 오케스트레이션 방식은 오케스트라 지휘자처럼 프로세스를 안내하고 구동하는 하나의 중앙 두뇌에 의존한다. 코레오그래피 방식은 발래 무용수들이 자신의 역할을 알고 주변의 다른 무용수에 반응하는 것처럼 시스템 각부분에 작업내용을 알리고 세부사항을 수행하게 한다.

오케스트레이션 필요한 가장 단순한 작업은 고객 서비스가 마치 중앙 뇌처럼 행동하게 하는 것이다. 고객서비스는 생성과정에서 일련의 요청/응답 호출을 통해 '적립 포인트 은행', '우편서비스','이메인 서비스' 와 통신한다. 이방식은 고객 서비스에 지나치게 많은 중앙 관리 권한이 부여되는 단점이 있다.

코레오그래피 고객 서비스가 비동기 방식으로 '고객이 생성되었다'라는 이벤트를 발산할 뿐이다. '적립 포인트 은행', '우편서비스','이메인 서비스' 가 이벤트를 구독하고 적절히 행동한다. 이방식은 확실히 느슨한 결합을 이끌어낸다. 이방식의 단점은 비지니스 프로세스의 명확한 뷰가 지금은 시스템에 암시적으로 반영되었다는 것이다.

필자는 코레오그래피 방식의 시스템이 전반적으로 더 느슨히 결합하고 유연하며 변경이 보다 쉽게 수용하는 것을 알게 되었다. 시스템 경계를 넘어 프로세스를 모니터링하고 추적하는 부가작업이 필요하지만 오케스트레이션 방식에 지나치게 의존하는 구현제는 매우 취약하고 높은 변경 비용을 수반한다.

지금부터 요청/응답을 고려할 때 아주 접한한 두가지 기술 PRC/REST에 대해 살펴보자

4.6 원격 프로시져 호출(Remote Procudure Call / RPC)

지역 호출을 통해 원격 서비스를 실행하는 기술 몇몇 다른 형태의 PRC 기술이 현업에 사용된다. 그중 일부 기술은 SOAP, 쓰리프트(Thrift), 프로토콜 버퍼(protocal Buffer)

이러한 RPC 구현체들은 여러분이 매우 빠르게 시작할 수 있는 클라이언트와 서버 스텁을 생성하게 된다. 필자는 네트워크 경계를 넘어 즉시 콘텐트(즉, 데이터)를 보낼 수있다. 이것이 RPC의 주요장점 중 하나인 '쉬운 사용성'이다. 일반적인 메서드 호출처럼 사용하고, 이론적으로 다른 것은 신경스지 않아도 된다는 점은 RPC가 주는 큰 혜택이다.

4.6.1 기술 결합

자바 RMI 와 같은 일부 RPC 메커니즘은 특정 기술과 지나치게 엮여 있어 클라이언트와 서버에 사용될 기술을 제한한다. 어떤 면에서 이러한 기술 결합은 내부의기술 구현 상세를 노출하는 한 형태일 수 있다. 예를 들어 RMI를 사용한다는 것은 JVM 이 클라이어트뿐만아니라 서버와도 관계가 있음을 의미힌다.

4.6.2 지역호출은 원격호출과 다르다

RPC 아이디어의 핵심은 원격 호출의 복잡성을 은폐하는 것이다. 하지만 RPC는 수많은 구현체가 이를 지나치게 은폐한다. 최악의 경우 추상화가 지나치게 불투명하다면(알 수 없다며) 개발자는 서비스 경계를 알지 못한 채 원격 호출을 사용할 수 있다.

우리는 네트워크 자체에 대해 생각해봐야한다. 분산 컴퓨팅에 대한 유명한 착오 중 첫번째는 '네트워크는 신뢰할 수 있다'는 것이다. 네트워크는 신뢰할 수 없다.

4.6.3 취성: 외부의 힘과 변화에 물체가 소성 변형을 거의 보이지 아니하고 파괴되는 현상으로 쉽게깨지거나 부서지는 성질

가장 대중적인 RPC 구현체들도 형편없이 깨지기 쉬운데, 자바 RMI 가 좋은 예다. 클라이언트와 서버배포를 분리할 수 없는 것은 바이너리 스텁 생성 방식을 장려하는 모든 RPC 메커니즘의 핵심 난제다.

4.6.4 RPC 는 형편없는가?

지금까지 설명한 RPC의 단점에도 불구하고 RPC 가 형편없다고 단정 짓기는 이르다. 많은 연산 작업은 RPC 기반의 모델이 적합하다. 이모델을 사용한다면 잠재적 함정을 알아야한다. 먼저, 네터워크가 은폐될 정도로 원격호출을 추상화 하지 말라. 그리고 서버와 보조를 맞춘 클라이언트의 업그레이드 없이도 서버 인터페이스를 발전시킬 수 있는지 확인하라.

데이터베이스 통합과 비교해보면 RPC는 요청/응답의 협업 방식 측변에서 확실히 개선되었다. 하지만 또 다른 대안도 고민해야 할 것이다.

4.7 REST

REST 는 웹에서 영감을 얻은 아키텍처 방식이다. 많은 원칙과 제약이 있지만, 여기서는 마이크로서비스 적용시 도울수 있는 부분만 집중할 것이다.

자원의 개념이 가장 중요하다. Customer 처럼 서비스 스스로 알고 있는 것을 자원으로 간주할 수 있다. 서버는 요청에 대한 Customer 의 다양한 표현을 생성한다. 자원이 외부에보여지는 방식과 내부에 저장되는 방식은 완전히 불리된다. 예를 들어 customer을 완전히 다른 포맷으로 저장하더라도JOSN 형태의 Customer 을 요청할 수 있다.

다양한 REST 방식을 비교한 리차드슨의 성숙모델을 꼭 살펴보라. 리차든슨의 성숙모델

4.7.1 REST 와 HTTP

HTTP 는 자체적으로 REST 방식과 궁합이 맞는 유용한 기능들을 정의한다. 예를 들어 GET, POST, PUT, 같은 HTTP 동사들은 자원과 함께 작동하는 방법에 관해 HTTP 명세서에 소개되어 잘알려져 있는 개념이다.

4.7.2 애플리케이션 상태 엔진으로서의 하이퍼미디어(HATEOAS:hypermedia as the engine of applicaton state)

클라이언트와 서버가 결합하지 않게 도와주는 또다른 원칙 HATEOAS 역시 REST 에 소개되어 있다.

하이퍼미디어란 : 하나의 콘텍트가 텍스트나 이미지, 사운드와 같은 다양한 포맷의 다양한 콘텐트를 링크하는 개념이다. 이것은 관련 콘텐트를 보기 위해 링크를 따라가는 것처럼 평범함 웹페이지와 유사한 방식으로 동작한다. HATEOAS 의 숨겨진 아이디어는 클라이언트가 다른 자원에 대한 링크를 통해 서버와 (잠재적으로 상태변이를 초래하는) 상호작용을 한다는 것 어떤 URI 를 요청했는지 알고 있으므로 서버 내 고객의 정확한 위치를 알 필요는 없다. 대신 클라이언트는 필요한 것을 발견하기 위해 링크를 찾고 탐색한다.

이는 다소 특이한 개념인 만큼, 우선 잠깐 뒤로 물러나서 사람들이 하이퍼미디어 컨트롤을 풍부히 가진 웹페이지와 어떻게 상호작용하는지 생각해보자

아마존 쇼핑사이트는 시간이 지나면서 쇼핑카트의 의치가 바뀌고 그래픽이나 링크도 변한다. 그러나 인간인 우리는 여전히 쇼핑카드를 찾을 수 있고, 이해하고, 상호작용할 수 있을 만큼 똑똑하다. 비록 쇼핑 카트를 표현하는 폼과 하부 컨트롤이 바뀌더라도 쇼핑 카트의 의미를 이해한다. 우리는 카트 목록을 보고 싶을 때 어떻게 접근해야하는지 알고 있다. 바로 이것이 웹페이지가 시간이 지나면서 점진적으로 변화할 수 있는 이유다.

우리는 하이퍼미디어 컨트롤을 통해 서비스 소비자들을 위한 동일 수준의 열리함을 달성하고자한다.

<album>
    <name> Give Blood </name>
    <link rel="/artist" href = "/artist/theBrackes" />
    <description> Awesome, short, brutish, funny and loud,. Must buy! </description>
    <link rel="/instantpurchase" href = "/instantpurchase/1234" />
</album>
  1. 이 하이퍼미디어 컨트롤은 아티스트에 대한 정보를 어디서 찾아야 할지 보여준다.
  2. 우리가 이 앨범을 구매하기 원한다면 이제는 어디로 갈지 알고 있다.

클라이언트는 앨범을 구매할 때 어떤 URI 스키마로 접근하는지 몰라도 된다. 단지 자원에 접근하여 컨트롤을 찾아 그것을 탐색하면 된다.

클라이언트와 서버의 결합을 피하기 위해 이러한 컨트롤을 사용하면 시간이 지남에 따라 프로토콜의 동작에 소요되는 약간의 시간을 크게 상쇄할 혜택이 발생한다. 클라이언트는 링크를 따라가면서 점진적으로 API를 발견하게 되는데 우리가 새로운 클라이언트를 구현할 때 매우 유요한 기능이 될수 있다.

4.7.3 JSON, XML 또는 다른것?

표준 텍스트 포맷을 사용하면 클라이언트는 자원을 유연하게 소비할 수 있으며 HTTP 상의 REST로 다양한 포맷을 사용할 수 있다. HTTP 상에서 작동하는 서비스의 경우에는 JSON 이 훨씬 대중적인 콘텐츠 타입이다.

다만 JSON은 몇가지 단점이 있다. JSON은 XML의 link 컨트롤 유사한 어떤 것도 정의하지 않기 때문에 해당 콘셉트를 넣기 위해 자체방식을 사용한다. HAL를 통해 몇가지 문제를 해결하기도 했다.

4.7.4 지나친 편의를 주의하라

REST의 인기가 높아지면서 RESTful 웹서비스 생성을 도와주는 프레임워크 역시 인기를 얻고 있다. 그러나 이들 도구 중 일부는 단기적으로는 득이 되지만 장기적으로는 고통을 준다는 점에서 일장일단이 있다. 작업속도를 높일 수는 있겠지만 몇가지 나쁜 행동을 범하게 된다.

4.7.5 HTTP 기반 REST 단점

하이퍼미디어 컨트롤을 클라이언트용으로 구현하고 사용하려면 꽤 많은 작업을 직접해야한다. 더 사소한 문제는 일부 웹 서버 프레임워크가 실제로는 모든 HTTP메서드를 제대로 지원하지 않는다는 점이다.

성능 또한 문제가 될 수 있다. HTTP 기반 REST 페이로드는 JSON 또는 바이너리 등의 포맷을 지원하므로 실제로 SOAP 보다 훨씬 간결할 수 있지만 쓰리프트와 같은 바이너리 프로토콜에는 상대가 되지 못한다. 그리고 각각의 HTTP 요청에서 늘 발생하는 HTTP부하 역시 낮은 지연시간이 요구될때 문제가 될 수 있다.

서버 대 서버 통신에서 극도로 낮은 지연시간 또는 작은 메시지 크기가 중요하다면 일반적으로 HTTP 통신은 좋은 생각이 아닐 수 있다.

이러한 단점에도 불구하고 HTTP 기반 REST 는 합리적이고 기본적인 선택이다. REST in Pracitce 추천

4.8 비동기 이벤트 기반의 협업 구현

여기서는 이벤트 기반의 비동기 통신을 살펴보자.

4.8.1 기술 선택

여기서 우리가 고려해야 할 두가지 주요문제는 마이크로서비스가 이벤트를 발산하는 방법과 소비자가 생성된 이벤트를 찾는 방법이다.

전통적으로 RabbitMQ 와 같은 메시지 브로커들은 두 문제를 모두 처리한다. 메시지 중개자가 미들웨어 세계에서 비록 작은 부분에 불과할 지라도 주의할 필요는 있다.

이벤트를 전파하는 또다른 대안은 HTTP를 사용하는 것이다. 아톰(ATOM) 은 자원의 피드를 계시하기 위해 의미를 정의한 REST 호환 사양서다. 많은 클라이언트 라이브러리가 이 피드를 생성하고 소비할 수 있다. 고객 서비스는 변경될 때 해당 피드에 대한 이벤트만 발생하고, 고객 서비스의 소비자들은 단지 피드의 변경이 있는지 폴링하면 된다.

여러분이 탄력적이고 괜찮은 메시지 중개자를 이미 사용하고 있다면 이벤트의 구독과 게시를 청리하는 데 사용할 것을 고려해보라. 하지만 아직 없다면 ATOM 을 살펴보라. 이때 매몰비용 오류에 유의해야 한다.

4.8.2 비동기 아키텍처의 복장성

이벤트 기방 아키텍처는 확실히 더 분리되고 확장가능한 시스템으로 우리를 인도한다. 그러나 이 같은 프로그래밍 방식이 복잡성을 더하는 것도 사실이다.

4.9 상태 기계로서의 서비스

REST 의 전문가가 되는 길을 택하든 아니면 SOAP 과 같은 RPC 기반 메커니즘을 고수하기로 결정하든 간에 '상태 기계로서의 서비스 '라는 핵심 개념은 매우 효과적이다.

고객 마이크로서비스는 고객의 모든 행동양식과 관련된 로직을 소유한다.

핵심 도메인 개념의 수명주기를 명확히 모델링하는 것은 매우 효과적이다. 예를 들어 이미 삭제된 고객 정보를 다른 곳에서 업데이트하려는 것과 같은 상태 충돌뿐만 아니라 상태 전이에 기반을 둔 행동도 한 곳에서 처리를 담당하느 것이다.

4.10 반응형 확장

흔히 Rx로 알려진 반응형 확장은 다수의 호출결과를 조합하고 그 결과에 따라 연산을 실행하는 메커니즘이다. 이들 호출은 그 자체로 블로킹 또는 논블로킹 호출이 될 수 있다.

내부적으로 Rx는 전통적인 흐름을 뒤집는다. 데이터를 요청하는 대신 데이터에 대한 연산을 수행하고, 그 연산의 결과를 관찰하며, 변경에 따라 반응한다. 일부 Rx 구현체를 ㅗㅇ해 여러분은 관찰 대상의 함수를 수행할 수 있다. 예를 들어 RxJava 에서는 map과 filter 같은 전통적임 함수가 수행될 수 있다.

4.11 마이크로서비스 세계에서 코드 재사용의 위험과 DRY

개발자인 우리가 가장 많이 들어본 축약어 중 하나가 DRY(Dont Repeat Youself) 일것이다. 때로는 DRY 가 중복된 코드를 회피하는 시도로 단순하게 정의되지만, 더 명확하게 정의하자면 시스템의 행동양식과 지식의 중복을 회피하는 모든 시도라고 할 수 있다. 이는 매우 합리적은 충고다.

우리는 DRY 를 통해 재사용할 수 있는 코드를 생성한다. 또한 중복된 코드를 추상화하여 여러 곳에서 호출할 수 잇게 만든다. 나아가 어디에서나 사용할 수 있는 공유 라이브러리도 만들 수 있다. 하지만 이러한 접근 방법은 마이크로 서비스 아키텍체에서 기만적인 위험이 될 수 있다.

반드시 피해야 할 위험중 하나는 마이크로서비스 와 소비자 간의 지나친 결합이며 마이크로서비스 자체의 작은 변경 사항 하나가 많은 소비자에게 불필요한 변경을 초래할 수 있다.

공유코드를 서비스 경계를 넘어서 사용한다면 잠재적인 결합의 문제를 안고 있는 셈이다. 하지만 로깅 라이브러리와 같은 공통코드는 외부에서 보이지 않는 내부의 개념이므로 사용해도 문제없다.

필자의 경험으로 얻은 규칙은 개별 마이크로서비스 내에서 DRY 를 위반하지 않아야 하지만 전체 서비스 간의 DRY 위반은 너무 걱정하지 않아도 된다는 것이다.

4.11.1 클라이언트 라이브러리

서비스에 관한 클라이언트 라이브러리의 작성이야 말로 서비스 구현 초기 시점의 필수 작업이라고 주장하는 많은 팀과 이야기를 나누었다. 클라이언트와 서버 API를 동일한 사람이 만들때의 문제는 서버에만 존재해야 할 로직이 클라이언트로 유입될 위험이 잇다는 것이다. 클라이언트에 더많은 로직이 스며들수록 응집력이 더많이 무너진다. 만약 클라이언트 라이브러리 방식이 여러분이 생각하는 것이라면 서비스 발견과 서비스 실패 등을 처리하는 하부 전송계층의 클라이언트 코드를 목적지인 서비스 자체의 것들과는 분리하는 것이 중요하다. 하부 API 호출을 위해 클라이언트 라이브러리의 사용을 강조할지 아니면 다른 기술 스택을 허용할 것인지를 결정하라. 그리고 최종적으로 클라이언트 라이브러리의 업데이트 시점을 클라이언트가 담당하게 하라. 각 서비스를 항상 독립적으로 릴리스 할 능력을 유지해야 하기 때문이다.

4.12 참조에 의한 접근

필자가 다루고자 하는 고려 사항 중 하나는 도메인 개체의 정보를 전달하는 방법에 관한것이다. 우리는 마이크로서비스 가 Customer와 같은 핵심 도메인 개체의 수명주기를 포함해야 한다고 봐야한다. 우리는 고객 서비스 안에 위치한 Customer 도메인 개체의 변경과 관련된 로직의 중요성을 얘기한 바있다. 그리고 변경을 원한다면 고객 서비스에 요청해야 한다는 것도 알고 있다. 결과적으로 고객 서비스를 Customer 개첻르의 참값의 근원으로 고려해야한다.

주문이 발송되면 이메일 서비스에 이메일 발신을 요청하는 예를 생각해보자. 지금은 우리의 요청을 고객의 이메일 주소, 이름, 주문 상세와 함꼐 이메일 서비스에 보낼 것이다. 그런데 이메일 서비스가 받을 요청을 큐에 넣거나 꺼내오는 사이에 요청된 데이터가 변경될 수 있다. 따라서 Customer 와 Order 자원에 대한 URI 만 보내고, 이메일 서버가 이메일을 보내는 시점에 해당 자원을 찾아와서 가져오도록 하는 방식이 더 타당하다. 여기서 이벤트 기반 협업을 고려한다면 멋진 대위법도 가능하다. 우리는 이벤트 발생 자체 뿐 아니라 무슨일이 일어났는지도 알아야한다. 예를 들어 우리가 Customer 자원변경에 따른 업데이트를 받고 있다면 해당 이벤트가 발생했을때 Customer 가 어떤 상태였는지 아는것이 중요하다. 개체에 대한 참조를 유지한다면 개체의 현재 상태를 알 수 있다. 따라서 우리는 참조와 이벤트 기반의 협업이라는 두 세계의 장점을 취할 수 있다.

4.13 버전관리

사람들은 서비스의 인터페이스를 결국에는 변경하게 될 것이라는 당연한 우려를 하면서 버전관리 방법을 이해하고 싶어한다. 이 문제를 더 세분화하고 처리할 수 있는 다양한 단계를 살펴보자.

4.13.1 가능하면 지연하기

호환성을 깨뜨리는 변경의 영향을 줄이는 가장 좋은 방법은 애초에 결함을 만들지 않는 것이다. 데이터베이스 통합은 호환성을 깨뜨리는 변경을 피하기 어려운 기술의 좋은 예이다. 반면 REST는 내부 세부구현을 변경하더라도 서비스의 인터페이스가 바뀌기 어렵기 때문에 이 문제를 회피하는 데 도움이 된다

이메일 보낼때 firstname, lastname, email, telephoneNumber 필드 사용하다. telephoneNumber 가 필요없다는 것을 알았을때 어떻게 해야하나? 그 필드를 제거해야 할까?! 이것은 소비자에게 불필요한 장애를 만들 수 있다.

서비스를 가능한 한 유연하게 소비하는 클라이언트를 만들려는 사례는 '전송할 때는 보수적으로, 받아들일 때는 자유롭게' 라는 포스텔의 법칙을 입중한다. 여기서 논의하는 요청과 응답의 상호작용 맥락에서는 소비되는 서비스가 우리에게 요청없이 스스로 변경하도록 최대한 허용할 것이다.

4.13.2 호환성을 깨뜨리는 변경 일찍 찾아내기

우리가 가능한 한 최고의 기술을 선택한다 해도 고장은 여전히 발생할 수 있기 때문에 소비자를 일찍 고장내는 변경은 최대한 빨리 발견하는 것이 중요하다. 문제를 일찍 발견할 수 있도록 필자는 제 7장에서 논의한 소비자주도계약을 지지한다.

4.13.3 유의적 버전 관리

클라이언트가 서비스의 버전 번호만 보고도 해당 서비스와 통합 가능한지 알 수 있다면 몃지지 않을까? 유의적 버전과리는 그것을 가능하게 하는 명세다. 이때 각버전은 MAJOR . MINOR . PATCH 형태가 된다. MAJOR 버전 번호의 증가는 하위 호환성이 깨진 변경이 발생했음을 의미 MINOR 버전 번호의 증가는 하위 호환성을 유지하면서 새로운 기능이 추가되었음을 의미 PATCH 번호의 증가는 기존 기능의 버그를 수정했다는 의미

4.14.4 다른 엔드포인트와 공존

인터페이스를 깨뜨리는 변경을 피하기 위해 할 수 있는 모든 것을 다 했다면 다음으로는 그 충격을 제한해야한다. 우리는 마이크로서비스 가 독립적으로 릴리스 하는 능력을 항상 유지하기 바라기 때문에 소비자에게 우리와 보조를 맞춰 업그레이드 하도록 강요하는 일을 피하고자 한다. 이를 위해 필자는 모든 신구 인터페이스가 현재 운영중인 서비스에 공준하도록 했다. 만약 호환성을 깨뜨리는 변경을 릴리스하고 싶다면 구버전의 엔드포인트와 신버전의 엔드포인트를 모두 노출하는 새로운 서비스 버전을 배포해야한다.

4.13.5 다수의 병행 서비스 버전 사용하기

자주 언급되는 또 다른 버전 관리 방법은 다양한 버전의 서비스를 동시에 실행하고 구 소비자의 트래픽을 구버전에, 신규 소비자를 신버전에 라우팅 하는 것이다. 이는 넷플릭스가 오래된 소비자를 변경하는 비용이 아주 높을 때 드물게 사용하는 방법으로, 개인적으로 좋아하지 않으나 넷플릭스가 여간해서 사용하지 않는 이유도 잘알고 있다. 첫째, 한 서비스의 내부 버그를 고치려면 두벌의 서로다른 서비스를 수정하고 배포해야한다. 둘째, 소비자가 찾는 서비스로 유도하기 위한 부가적인 로직이 필요하다. 셋째, 그 서비스가 처리해야 할 영속적 상태가 있는지 고려해야 한다.

4.14 사용자 인터페이스

필자와 동료들은 더많은 로직이 서버 측에 있는 가변운 UI 를 생각하기 시작했다. 하지만 시간이 지나면서 자바스크립트는 브라우저 기반의 UI 에 동적 행위를 추가하며 더욱 인기 있는 대안으로 떠올랐고, 일부 애플리케이션은 오래 전에 데스크톱 클라이언트만큼 무거워져서 최근 논란이 되고 있다.

4.14.1 디지털을 향해

최근 몇년 동안 많은 조지이 웹과 모바일을 다르게 다뤄야 한다는 고정관념에서 벗어나기 시작했다. 대신 더 포괄적인 관점에서 디지털을 생각했다. 마침내 우리는 고객이 우리 회사와 어떻게 상호작용할지 정확히 예측할 수 없다는 것을 이해한 후 마이크로서비스가 제공하는 것과 같은 더 세분화된 API를 채택하게 되었다.

4.14.2 제약

제약은 사용자가 시스템과 상호작용하는 또 다른 형태다. 예를 들어 데스크톱 웹 어플리케이션에는 방문자가 사용하는 브라우저의 종류와 해상도 등의 제약이 있고, 특히 모바일에는 완전히 새로운 제약들이 존재한다. 상호작용의 성질 역시 달라진다. 태블릿 에서는 PC 처럼 쉽게 오른쪽 버튼클릭을 할 수 없다. 모바일폰에서는 대부분의 동작을 엄지 손가락으로 수행하며 한손으로 사용가능한 인터페이스를 원할 것이다.

따라서 우리의 주요 제품인 헥심 서비스들이 동일하더라도 각 인터페이스의 다양한 제약에 따라 그 서비스들을 적절히 적용할 방법이 필요하다.

4.14.3 API 구성

우리 서비스가 HTTP 상에서 이미 XML 이나 JSON으로 서로 통신하고, 우리에게 주어진 옵션은 사용자 인터페이스가 직접 API 통신하는 것이라고 가졍해보자. 다양한 종류의 디바이스에 맞춤화된 응답 능력이 거의 없다. 예를 들어 고객 레코드를 추출할 때 헬프데스크 애플리케이션에 응답하는 것과 똑같은 데이터를 모바일 쇼핑몰에 되돌려줘야 할까? 여기서 한가지 해결책은 소비자가 요청할 필드를 명시하는 것이다. 하지만 이것은 각서비스가 이러한 형태의 상호작용을 지원한다는 가정하에서 가능하다.

4.14.4 UI 부분 구성

UI가 API 를 호출하고 UI 컨트롤과 매핑하는 대신 서비스들이 UI 생성에 필요한 일부 UI 를 직접 제공할 수 있다.

4.14.5 프론트엔드를 위한 백엔드

백엔드 서비스와의 호출이 많은 인터페이스 문제나 각종 디바이스별 콘텐트의 맞춤화 요구에 대한 일반적이 해결책은 서버 측의 집합 엔드포인트 또는 API 게이트웨이를 제공하는 것이다. 이경우 다수의 백엔드 호출을 마샬링 할 수 있고, 서로 다른 종류의 디바이스를 위해 필요하다면 콘텐트에 변화를 주거나 집계하여 표현할 수 있다. 하지만 필자는 서버 측 엔드포인트가 너무 많은 동작을 하며 계층이 두터워지면 이러한 방식이 쟁앙으로 바뀌는 것을 목격했다.

발생할 수 있는 문제는 모든 서비스에 대한 거대한 단일 계층이 될 수 있다는 것이다. 이는 모든것을 한장소에 밀어 넣고 다양한 사용자 인터페이스의 분리를 어렵게 만들며, 독립적으로 릴리스 할수 있는 능력을 제한한다. 필자가 선호하며 제대로 작동했던 모델은 백엔드를 특정 사용자 인터페이스 또는 애플리케이션으로 제한하는 것이다.

프론트엔드를 위한 백엔드 라고 하는 패턴에서는 특정 UI에 집중하는 팀이 해당 서버측 컴포넌트도 담당한다. 이 방법의 위험성은 집합계층의 위험성(포함해서는 안되는 로직을 포함할 수 있다)과 유사하다. 따라서 백엔드가 사용하는 다양한 기능의 비지니스 로직은 서비스에 존재해야 하고 BFF 는 특정 사용자 경험을 제공하기 위한 행동 양식만 포함해야 한다.

4.14.6 하이브리드 방식

여기서 핵심은 우리가 사용자에게 제공하는 하부 기능들의 응집력을 유지해야 한다는 것이다.

4.15 외부 소프트웨어와 통합

지금까지 통제하에 있는 시스템들을 분리하는 방법을 살펴보았다. 변경할 수 없는 경우라면 어떨까?

여러분 조직이 엄청나게 많은 자체 소프트웨어를 만들어낼 능력이 있다 하더라도 상용제품 또는 오픈소스같은 외부 소프트웨어 제품을 여전히 사용할 것이다. 왜그럴까? 첫째, 조직이 필요로하는 소프트웨어 수요량은 내부에서 충당할 수 있는 범위를 넘어선다. 둘째, 비용면에서 효과적이지 않다. 이는 가장 중요한 이유다.

4.15.1 통제부족

CMS 또는 SaaS 도구와 같은 상용제품의 기능을 통합하고 확장하는 일과 관련한 한가지 난제는 수많은 기술적 결정이 이미 정해져 있다는 것이다. 만약 운이 좋다면 개발관점에서 해당 도구와 작업하는 것이 얼마나 쉬울지 아니면 어려울지 도구 선택과정에서 이미 고려했ㅇ을 것이다. 하지만 그렇다 해도 어느 정도의 통제권을 외부업체에 양도하고 있는 셈이다. 이때 요령은 통합과 맞춤활ㄹ 여러분 방식 안으로 끌어들이는 것이다.

4.15.2 맞춤화

기업에서 구매하는 많은 도구는 여러분을 위해 훌륭히 맞춤화 될 수 있다고 광고하며 판매된다. 하지만 주의하라. 흔히 접근할 수 있는 도구 체인의 특성상 맞춤화의 비용은 처음부터 맞춤개발을 한 것보다 비쌀것이다.

4.15.3 스파케티 통합

또 다른 문제는 외부 도구와의 통합 방법이다.

4.15.4 여러분 방식대로

상용제품(COTS) 와 SaaS 제품은 분명한 제위치가 있으며, 우리가 대다수가 처음부터 개발하는 일은 가능하지도 합당하지도 않다. 그러면 문제를 어떻게 해결할까? 그 비결은 여러분 고유의 방식으로 되돌아가는 것이다.

4.15.5 교살자 패턴

여러분에게 완전한 통제권이 없는 레거시 플랫폼 또는 상용 플랫폼의 경우 이를 제거하거나 또는 그로부터 이전하려 할때 발생하는 문제를 잘 처리해야한다. 이때 유용한 패턴이 교살자 애플리케이션 패턴이다.

CMS의 앞단을 자체 코드로 작성했던 사례와 흡사하게 교살자를 통해 구버전 시스템에 대한 호출을 잡아내 가로챈다. 교살자는 이 호출을 기존의 레거시 기존의 레거시 코드 또는 여러분이 작성한 새로운 코드로 바로 경유할지 결정할 수 있다. 그리고 엄청난 뷴량의 코드 재작성 없이도 한번에 기능을 교체해준다.

4.16 마치며

이장에서는 통합과 관련된 여러 방안을 살펴봤고, 마이크로서비스를 협업자와 가능한 한 분리하기 위해할 수 있는 가장 적합한 선택에 대해 필자의 견해를 공유했다.

  • 데이터베이스 통합은 최대한 피하라.
  • REST 와 RPC의 장단점을 이해하고 요청/응답을 통합하는 좋은 출발점으로 REST를 고려하라.
  • 오케스트레이션보다는 코레오그래피를 우선하라.
  • 포스텔의 법칙을 이해하고 관대한 독자 태턴을 사용해서 호환성을 깨뜨리는 변경과 불필요한 버전을 피하라
  • 그성 계층으로서의 사용자 인터페이스를 생각하다.

Chapter 5 모놀리스 분해하기

모놀리스는 시간이 흐르면서 자라나고, 빠른 속도로 새로운 기능과 코드라인을 요구하며, 머지않아 조직 내 사람들이 건드리거나 바꾸기 두려워하는 무서운 존재가 된다. 그러나 전혀 희망이 없는 것은 아니다. 우리는 자유자재로 다룰 수 있는 올바른 도구를 통해 이 야수를 없앨 수 있다.

5.1 접합부가 중요하다

우리는 응집력이 높고 느슨히 결합되는 서비스를 원한다고 이야기했다. 하지만 모놀리스 문제는대개 이와는 너무도 정반대라는 것이다.

마이클 페더스의 레거시 코드 활용전략 에서는 접합부(seam) 의 개념을 '코드베이스의 나머지 부분에 영향을 주지 않는 격리된 코드 부분' 이라고 정의한다. 코드베이스를 정리할 목적으로 접합부를 찾기보다는 서비스의 경계가 될 수 있는 접합부를 발견하기 원한다.

무엇이 좋은 접합부를 만들까? 이전에 논의했던 것처럼 경계가 있는 콘텍스트는 훌륭한 접합부를 만든다. 경계가 있는 콘텍스트의 정의가 조직 내의 응집력 있고 느슨히 결합된 경계를 잘 표현하기 때문이다.

5.2 뮤직코퍼레이션 분해하기

뮤직코퍼레이션 온라인 시스템이 아주 많은 행동약식을 구성하는 거대한 모놀리식 서비스 백엔드가 있다고 가정하자. 모놀리식 백엔드가 포함하고 있다고 생각되는 4개의 콘텍스트를 초기에 파악했다고 하자.

제품목록 : 판매하는 제품에 대한 모든 메타데이터
재무 : 계정, 결제 환불 등의 보고
창고 : 고객 주문 발송 및 반품, 재고 수준 관리 등
추천 : 특허 출원 중이며 획기적인 우리의 추천 시스템으로 일반 여구소보다 박사 비율이 높은 팀이 매우 복잡한 코드로 작성함 

처음해야 할일은 이 콘텍스트들을 대표하는 패키지를 생성하는 것이다. 그다음 기존 코드를 패키지로 이동시킨다.

이과정에서 우리는 패키지 간의 의존성을 분석할 수 있는 코드를 사용할 수 있다. 우리가 만든 코드는 우리 조직을 태표해야 마당하므로 경계가 있는 콘텍스트를 대표하는 패키지들은 실세계에서 도메인과 같은 방식으로 상호작용 해야한다.

5.3 모놀리스를 분리하는 이유

모놀리식 서비스를 더 작게 만들려는 결정은 좋은 출발점이다. 그러나 필자는 시스템을 조금씩 깍아내듯이 분리하기를 권고한다.

끌질을 잘할 수 있도록 안내하는 몇가지 추진 동력을 생각해 보자

5.3.1 변경의 속도

재고 목록을 관리하는 과정에서 앞으로 발생할 변경으로 인한 부담을 알고 있을 것이다. 따라서 지금 창고의 접합부를 서비스로 분리한다면, 그 접합부는 독립된 자율적개체가 되어 더욱 신속하게 변경 가능하다.

5.3.2 팀구조

뮤직코퍼레이션 의 제품 출시 팀은 두지역 런던, 하아외 에 있다. 만약 하와이 팀이 주로 작업하는 코드를 분리해서 모든 소유권을 가질 수 있다면 정말좋을 것이다.(10장에서 자세히)

5.3.3 보안

만약 서비스를 분리한다면 개별 서비스를 모니터링할 수 있고 데이터의 전송과 저장에 있어 추가적인 보호를 할 수 있다.(9장에서 자세히)

5.3.4 기술

추천 시스템을 담당하는 팀은 클로저 언어로 작성된 라이브러리를 이용하여 몇가지 새로운 알고리즘을 개발해왔다. 만약 추천시스템의 코드를 분리해서 독립된 서비스로 만들 수 있다면 그것을 테스트할 수 있는 다른 구현 방식을 쉽게 고려할 수 있다.

5.4 뒤엉킨 의존성

분리할 접합부 몇개를 인식했을 때 고려할 또 다른 점은 그 코드가 시스템의 나머지 부분과 어떻게 엉켜져 있는가 하는 것이다. 뒤엉킨 모든 의존성을 출처가 대개 데이터베이스 라는 것이다.

5.5 데이터베이스

우리는 데이터베이스에서 접합부를 찾아야 깔끔하게 분리할 수 있지만 데이터 베이스는 길들이기 매우 어려운 야수다.

5.6 문제에 대처하기

첫번째 단계는 코드 자체를 살펴보고, 데이터베이스에 읽고 쓰는 코드 부분을 보는 것이다.

특정 콘텍스트의 코드 내에 데이터베이스 매핑 코드를 함께 배치하면 어떤 코드가 데이터베이스의 어느부분을 사용하는지 이해할 수 있다.

우리는 경계가 있는 콘텍스트를 발견했고, 네 개의 구분되고 협업하는 서비스를 만들면서 나아가길 원한다. 이때 대면하게 되는 몇가지 구체적인 문제와 가능한 해결책을 살펴볼 것이다.

5.7 예 : 외부 키 관계 깨뜨리기

매월 말 조직 내 다양한 사람에게 우리가 하는 일을 보여주기 위해 보고서를 만들 필요가 있다. 'SKU 12345 제품을 400 장 판매해서 1300 달러의 수익을 냈습니다.' 대신 '브루스 스플링스틴의 베스트 음반을 400장 판매해서 1300 달러의 수일을 냈습니다.'

재무 패키지의 보고서 코드 SKU 에 해당하는 앨범 제목을 가져오도록 행 항목 테이블에 접근할 것이다. 이따 원장테이블에서 행 항목테이블에 대한 외부키 제약이 발생할 수 있다. 해결하기 위해선 두군데 변경이 필요하다.. 우성 행 항목 테이블이 제품 목록코드에 속하기 때문에 재무 코드가 이 테이블에 접근하는 것을 중단 시킨다. 이문제를 신속히 해결하기위해 재무 코드가 행 항목 테이블에 접근하는 방법 보다는 재무코드가 호출할 수 있는 제품목록 패키지의 API를 통해 데이터를 노출한다.

이때 보고서를 생성하기 위해 두개의 데이터베이스에 호출 해야하는 것은 분명하다. 늘 그렇듯이 성능에 대한 우려가 제기되지만 이에 대한 꽤 쉬운 대답이 있다. 여러분은 얼마나 빠른 시스템이 필요한가? 만약 시스템의 현재 성능을 테스트할 수 있고 좋은 성능이 어느 정도인지 알 수 있다면 변경하는 데 확신이 설 것이다.

하지만 외부 키 관계는 어떤가? 그관계도 함께 사라졌다. 이것은 데이터 베이스 수준이 아닌 결과적으로 얻어진 서비스가 관리해야하는 제약 조건이다.

5.8 예 : 공유 정적 데이터

데이터베이스에 저장된 공유 정적 데이터의 사례는 많이 볼 수 있다. 행후에 뮤직 쇼핑몰의 모든 서비스가 이런 동일한 테이블을 참조하게 된다면 어떻게 해야할까?

몇가지 대안이 있다. 첫째는이 테이블을 각 페키지에 복제하는 것이다. 장기적으로 보면 그 테이블은 각 서비스 내부에서도 복제될 수 있다. 물론 이것은 잠재적으로 일관성 문제를 초래한다. 둘째는 공유 정적 데이터를 코드로 다루는 것이다. 이것은 아마도 서비스의 부분으로 배포되는 속성파일에 저장되거나 열거형 개체가 될 수 있다. 동작중인 데이터베이스 테이블을 변경하는 것보다 설정 파일을 변경하는 것이 훨씬 용이하겠지만, 여전히 대이터 일관성 문제는 존재한다. 셋째는 극단적일 수 있는데, 이 정적 데이터를 특정 독립적인 서비스 안에 삽입하는 것이다.

5.9 예 : 공유 데이터

좀 더 복잡한 예를 살펴보자. 변경가능한 공유 데이터는 여러분이 시스템을 분리할때 자주 발생하는 문제가 될 수 있다.

재무와 창고 코드는 동일한 테이블을 읽고 쓰게 된다. 이것을 어떻게 분리할 수 있을까?! 실제로 여기에 있는 것은 여러분이 앞으로 자주보게될 도메인 개념(코드에서 모델링 되지 않고 데이터베이스에서 암묵적으로 모델링되는)이다 여기수 누락된 도메인 개념은 Customer 이다.

우리는 추상화된 고객 개념을 생성해야 한다. 임시 단계로 , Customer 로 명명된 새로운 패키지를 만들고 Customer 코드를 재무 또는 창과와 같은 다른 패키지에 노출 시키기 위해 API를 사용할 수 있다. 결과적으로 별도의 고객 서비스가 만들어진다.

5.10 예 : 공유 테이블

모든 코드를 병합하기 전에는 관심사들이 실제로 통합되었는지 명확하지 않다. 지금은 다르게 저장될 수 있는 분리된 두가지 개념이 있음을 알 수 있다. 이에 대한 해결책은 테이블을 분리하는 것이다.

5.11 데이터베이스 리팩토링

앞의 예에서다루었던 것은 스키마를 분리하는 데 도움이 되는 몇가지 데이터베이스 리팩토링 방법이 있다.

5.11.1 단계적 분리

필자는 애플리케이션 코드를 완전히 분리하여 각각의 마이크로서비스로 만들기 전에, 서비스는 이전과 같이 하나로 유지한체 우선 스키마를 분리할 것을 추천하고 싶다.

  1. 단일 스키마 -> 2. 스키마 분리 -> 3. 애플리케이션을 서비스로 분

스키마가 분리되면 단일 작업을 수행하는 데이터베이스의 호출 횟수가 증가할 수 있다. 또한 두개의 스키마로 분리하면 결국 트랜잭션 일관성을 깨뜨리게 된다. 이는 애플리케이션에 지대한 영향을 미칠 수 있으며 바로 다음에 논의할 주제다.

5.12 트랜잭션 경계

트랜잭션 은 유용하다. 이벤트가 모두 발생하거나 전혀 발생하지 않게 해준다. 트랜잭션은 데이터베이스에 데이터를 입력할 때 매우 유용하다. 한번에 다스 테이블의 업데이트가 가능하고, 잘못된 경우에는 모든 것을 롤백함으로써 데이터의 불일치 상태를 피할 수 있기 때문이다.

모놀리식 스키마의 경우 아마도 모든 생성과 업데이트는 단일 트랙잭션 경계 내에서 수행될 것이다. 따라서 데이터베이스를 분리할 경우 단일 트랙잭션에 의해 제공된 안정성을 읽는다.

만약 주문테이블에 삽입하는데 실패하면 일관성을 유지한 상태에서 모든것을 확실히 중단할 수 있다. 그러나 주문 테이블에 삽입하는 데는 성공했지만 수집 테이블에 삽입하는데 실패하면 어떻게 될까?

5.12.1 나중에 재시도하기

우리는 이 연산의 일부를 큐나 로그 파일에 큐일하여 나중에 재시도 할 수 있다.

이것을 여러모로 최종적 일관성의 또 다른 형태다. 트랜잭션이 완료 되었을 때 시스템이 일관성을 유지하는 상태임을 보장하기 위해 트랜잭션 경계를 사용하는 대신 향우 특정 시점에 시스템이 스스로 일관성을 유지하는 상태가 될 수 있음을 허용한다. 이 접근 방식은 비지니스 작업들이 오래 지솔될 경우 특히 유용하다.

5.12.2 전체 작업 중단하기

다른 방안은 전체 연산 작업을 중지하는 것이다. 이 경우 시스템을 다시 일관성이 유지된 상태로 복귀 시켜야 한다. 우리가 해야 할일은 보상 트랜잭션을 발행하는 것으로, 직전의 트랜잭션을 되돌릴 새로운 트랜잭션을 발생시키는 것이다.

그러나 이 보상 트랜잭션이 실패하면 어떻게 될까? 확실히 가능한 시나리오다. 그러면 수집 명령과 일치되지 않는 주문이 주문테이블에 있게 된다. 이런 상황에서는 보상 트랜잭션을 재시도하거나 일관성이 유지되지 않는 것을 정리하는 백엔드 프로세스를 수행할 필요가 있다.

일관성이 유지 되기 바라는 작업이 한두개가 아니라면, 각각의 실패 모드에 대해 보상 트랜잭선을 처리하는 것은 구현은 고사하고 이해하기도 매우 어렵다.

5.12.3 분산 트랜잭션

분산 트랜잭션을 수동으로 통제하는 방식의 대안은 분산 트랜잭션의 사용이다. 분산 트랜잭션은 하부 시스템에서 수해되는 다양한 트랜잭션을 통제하기 위해 트랜잭션 관리자라는 전체적인 통제 프로세스를 사용해서 트랜잭션 내부의 여러 트랙잭션을 확장하려 시도한다. 일반적인 트랜잭션과 마찬가지로 분산 트랜잭션도 일관된 상태를 유지하도록 노력한다. 그리고 일관된 상태가 유지되는 경우에만 다른 시스템의 다른 프로세스에서도 실행되도록 한다. 이경우 종종 네트워크 경계를 넘어 통신한다.

분산 트랜잭션을 처리하는 가장 범용적인 알고리즘은 2단계 커밋을 사용하는 것이다. 2단계 커밋을 사용하면 먼저 투표 단계가 시작된다. 이 단계에서 각 참여자(분산트랜잭션 에서는 코호트라고도 함)는 로컬 트랜잭션의 진행 가능 여부를 트랜잭션 매니저에게 알린다. 만약 트랜잭션 매니저가 모든 참여자로부터 찬성표를 얻는다면 참여자에게 각자의 커밋을 수행하라고 지시한다. 트랜잭션 매니저가 반대표를 하나라도 받는다면 모든 참여자에게 롤백 명령을 보낸다.

이 방법에서는 중앙의 조정 프로세스가 진행을 허락할 때가지 모든 참여자는 중지해야하는데 이는 장애에 취약함을 의미한다. 만약 코호트가 투표중에 응답하지 못하면 모든것이차단된다. 이런 조정과정은 잠금을 의미하기도 한다. 즉, 대기중인 트랜잭션은 자원을 잠금상태로 유지할 수 있다. 자원의 잠금은 특히 분산 시스템에서 시스템의 확장을 더 어렵게 만들고 자원에 대한 경쟁을 초래할 수있다.

분산 트랜잭션은 자바 트랜잭션 API 와 같은 특정 기술 스택으로 구현되어 왔으며 데이터베이스와 메시지큐처럼 서로 상이한 자원을 모든 참여자가 동일하며 중요한 트랜잭션으로 사용할 수 있게 했다.

5.12.4 그렇다면 무엇을 해야 할까?

이러한 솔류션들은 복잡성을 증가시킨다. 분산트랜잭션을 제대로 수행하는 것은 쉽지 않으며 실제로 확장성을 저해할 수 있다.

정말로 일관성이 유지되어야 하는 상태가 필요하다면 처음부터 그 상태가 유지되도록 할 수 있는 모든 것을 하라. 그리고 정말로 열심히 노력하라. 불가피하게 분리를 감수해야 한다면 단지 그과정의 기술적 관점에서 벗어나 트랜잭션 자체를 표현할 구체적인 개념을 만들어라. 이것은 보상 트랜잭션과 같은 다른 작업을 실행할 여지를 주고 시스템의 복잡한 개념들을 모니터링 하고 관리할 수 있는 방법을 제공한다. 예를 들면, '진행중인 주문' 같은 개념을 만들 수 있다

5.13 리포팅

앞에서 이미 살펴본것처럼 특정 서비스를 더 작은 부분으로 분리하는 데 있어 데이터의 저장 방법과 장소를 자재적으로 분리할 필요가 잇다. 이는 일반적이고필수적인 사용사례인 리포팅에 문제가 된다.

마이크로서비스 아키텍처로 바꾸는 것과 같은 근본적인 아키텍처 변경은 엄청난 혼란을 초래할 수 있지만 우리가 하는 모든것을 포기해야 하는 것을 의미하지는 안는다.

리포팅 시스템을 이용하는 대상은 평번한 사용자고, 우리는 그들의 요구를 고려해야 한다. 아키텍처를 근본적으로 변경하고 사용자에게 단순히 그에 순응하기를 요청하는 것은 다소 오만할 수 있다.

5.14 리포팅 데이터베이스

리포팅은 유용한 결과를 생성하기 위해 조직 내 여러 부분의 데이터를 함께 모을 필요가 있다. 예를 들어 제품목록과 판매설명이 있는 일반적인 원장에 더 풍부한 데이터를 추가하거나 특정 VIP고객들의 정보와 구매내역을 포함한 구매 행동 패턴을 보고 싶을 것이다. 모놀리식 서비스에서는 단일 데이터베이스에 저장되므로 모든 정보의 경계를 넘어서 리포팅하는 것이 실제로 매우 쉽다.

일반적으로 우리는 리포팅 질의가 메인 시스템의 성능에 영향을 주는 것을 우려하여 리포트를 메인 데이터베이스에 실행하지 않을 것이다. 따라서 이러한 리포팅 시스템은 대게 읽기용 복제 데이터베이스와 연결된다.

이미 모든 데이터가 한곳에 있어서 매우 단순한 질의도구로 사용할 수 있는 아주 큰 장점이 있지만 몇가지 단점도 있다. 첫째, 여기서 데이터베이스 스키마는 실행중인 모놀리식 서비스와 리포팅시스템 사이에서 사실상 공유 API 다. 따라서 스키마의 변경은 조심스럽게 관리 되어야 한다. 둘째, 실제 시스템 또는 리포팅 시스템을 지원하는 사용 사례를 위해 데이터베이스를 최족화하는 방법이 제한적이다. 셋쩨, 우리가 사용할 수 있는 데이터베이스 방법이 최근에 폭발적으로 증가했다. 하지만 일반적인 관계형 데이터베이스가 많은 리포팅 도구와 호환되는 SQL 질의 인터페이스를 제공하더라도 동작중인 서비스에 데이터를 저장하기 위한 최선의 방법은 아니다.

실제 시스템과 리포팅 시스템용으로 하나의 데이터베이스만 사용할 수 있도록 제한하면 대개 데이터베이스간의 선택과 새로운 대안의 탐구가 어려워진다. 우리 정보가 서로다른 시스템에 저장되어 있다면, 리포트 생성을 위해 모든 데이터를 한곳에 모을 방법은, 일반적인 리포팅 데이터베이스 모델과 연관된 몇몇 단점을 제거할 방법은

이제 필자가 실제로 경험한 몇가지 방안을 알아볼 것이다.

5.15 서비스 호출을 통한 데이터 추출

다양한 변형 모델이 있지만 모두 원본 시스템으로부터 API 를 통한 데이터 추출에 의존한다. 그러나 이 접근 방식은 대용량의 데이터를 필요로 하는 사용 사례의 경우 바로 실패한다.

리포팅 시스템 역시 특정 방법으로 데이터를 추출하는 외부 도구에 자주 의존한다. 그리고 이때 SQL 인터페이스를 제공하면 리포팅 도구 체인을 가능한 한 손쉽게 통합하기 위한 가장 신속한 방법이 된다. 데이터를 주기적으로 추출하여 SQL 데이터베이스에 저장하기 위해 여전히 이방법을 사용할 수 있지만 몇가지 문제가 있다.

주요 문제 중 하나는 다양한 마이크로서비스에 의해 노출되는 API는 리포팅 용도로 설계된 것이 아닐 가능성이 있다는 것이다. 예를 들어 고객 서비스는 ID 또는 다양한 고객 필드로 고객을검색할 수 있게 허용하지만, 모든 고객을 추출하는 API를 반드시 외부에 공개하는 것은 아니다. 따라서 모든 데이터를 추출하기 위해 많은 API 호출을 해야 할 수도 있다.

서비스에 의해 노출되는 자원에 캐시 헤더를 추가하면 일부 데이터의 추출 속도를 높이고 리버스 프록시와 같은 장소에 이 데이터를 캐시할 수 있지만, 리포팅은 흔히 장기간의 데이터에 접근하는 특징이 있다. 따라서 아무도 이전에 요청하지 않았던 자원을 요청할 수 있으며 이로 인해 잠재적으로 연산 비용이 높은 캐시 미스가 발생한다.

리포팅을 쉽게 만들기 위해서는 배치 API를 제공해서 이 문제를 해결할 수 있다. 예를 들어 고객서비스는 배치를 통해 고객 ID 리스트를 전달받아 고객정보를 추출하거나 모든 고객 정보를 페이지 단위로 제공하는 인터페이스를 노출할 수 있다. 이방법의 극단적인 버전은 배치 요청을 자원으로 자체 모델링하는 것이다.

5.16 데이터 펌프

리포팅 시스템이 데이터를 끌어오는 방식 대신 리포팅 시스템에 데이터를 밀어 넣는 방식을 시도할 수 있다. 일반적인 HTTP 호출로 데이터를 추출하는 데 있어 단점 중 하나는 다수의 호출을 할 때 발생하는 HTTP 의 부하고 리포팅 목적으로만 사용될지도 모르는 API 를 만들어야 하는 부담이다. 다른 대안은 데이터의 소스인 서비스의 데이터베이스에 직접 접근하여 리포팅 데이터베이스로 밀어넣는 독립 프로그램을 가지는 것이다.

데이터펌프를 시작하려면 해당 서비스를 관리하는 팀이 그것을 개발하고 관리해야 한다. 이것은 Cron 과 같이 단순한 명령행 프로그램이 될 수 있으며, 해당 서비스의 내부 데이터베이스와 리포팅 스키마 모두 잘 알고 있어야 한다. 펌핑 작업은 스키마 하나를 다른 것과 매칭한다.

우리는 서비스를 관리하는 팀이 데이터 펌프도 관리하게 함으로써 서비스 스키마와 결합되는 문제를 줄이도록 노력한다. 필자는 사실 여러분이 서비스와 데이터 펌프 중 하나가 배포될때 같으 배포할 것을 가졍해 버전을 함께 관리하고, 데이터 펌프를 서비스 자체 빌드의 일부로 추가해 부차 결과물로 생성할 것을 제안한다.

리포팅 스키마 자체의 결함 문제가 남아있지만 그것을 변경하기 어려운 게시된 API 처럼 다루어야 한다. 일부 데이터베이스는 이러한 수고를 더 줄일 수 있는 기술을 제공하기도 한다.

5.16.1 대체 종착지

필자가 참여한 한 프로젝트에서는 ASW S3 를 실제로 거대한 데이터 마트로 위장하며, JSON 파일을 S3 에 저장하기 위해 일련의 데이터 펌프를 사용했다. 이 방법을 우리 솔류션이 확장이 필요할 때까지 아주 효과가 있었고, 이글을 쓰고 있는 현재 엑셀과 태블로 같은 표준 리포팅 도구와 통합할 수 있는 큐브를 채우도록 이 펌프들을 변경하는 것을 검토하고 있다.

5.17 이벤트 데이터 펌프

4장에서 이벤트를 발생하는 마이크로서비스의 아이디어에 대해 다루었다. 이벤트 피드를 노출하는 마이크로서비스에 대해 리포팅데이터베이스로 데이터를 밀어 넣는 자체의 이벤트 구독자를 작성할 수 있다.

이제 고객 서비스와 같은 원보 마이크로서비스의 하부 데이터 베이스와 결합되지 않는다. 대신 해당 서비스에 의해 발산된 이벤트와 바인딩한다. 이벤트가 본래 순간적임을 고려하면 이것은 중앙의 리포팅 저장소에 우리가 어떤 데이터를 보낼지 더 현명하게 정할 수 있게한다. 이벤트가 발생할 때 리포팅 시스템에 데이터를 보낼 수 있으므로 정기적으로 데이터를 밀어 넣는 것보다 신속하게 데이터가 리포팅 시스템에 흘러갈 수 있다.

이벤트 데이터 덤프는 서비스 내부와 약하게 결합되므로 해당 마이크로 서비스를 담당하는 팀이 아닌 다른 그룹이 이벤트 데이터 덤프를 관리하기 더 쉽다고 생각할 수 있다. 이벤트 흐름의 특성이 구독자와 서비스의 변경을 지나치게 결합하지 않는 한 이벤트 매퍼는 가입된 서비스와는 독립적으로 진화할 수 있다.

5.18 백업 데이터 펌프

이 방안은 넷플릭스에서 사용되며 기존의 백업 솔류션을 활용하여 그들이 겪은 몇가지 확장성 문제도 해결한다. 어떤 면에서 이것을 데이터 펌프의 특별한 사례로 생각할 수도 있지만, 포함할 만한 흥미로운 솔류션으로 보인다.

넷플릭스는 그들의 많은 서비스를 위한 자료 저장소로 카산드라를 표준화하기로 결정했다. 넷플릭스가 저장하는 데이터를 적절하게 백업하는 것은 매우 중요하다. 넷플릭스는 SSTable로 알려져 있는 이런파일들을 상당한 수준의 데이터 내구성을 자랑하는 AWS 의 S3 객체 저장소에 저장한다.

넷플릭스는 이 모든 데이터를 리포트해야 하는데 이와 연관된 리포팅 규모는 사소한 문제가 아니다. 그들은 SSTable에 백업된 것을 작업원본으로 사용하는 하둡을 통해 방대한 양의 데이터 처리할 수 있는 파이르파인을 현했으며 오픈소스화 되었다.

5.19 실시간을 향해

앞에서 요약한 많은 패턴은 다양한 장소에서 한 곳으로 수많은 데이터를 가져오는 다양한 방법이다. 그로나 모든 리포팅을 한곳에서 완성한다는 생각은 더이상 타당하지 않은 걸까?

5.20 변경비용

필자가 이책을 통해 작고 점진적인 변경의 필요성을 장려하는 많은 이유가 있지만, 주요 요인 중 하나는 우리가 만드는 각각의 수정과 필요할 경우 방향을 변경할 때의 영향도를 이해하는 것이다. 이는 실수의 비용을 더 완화하지만 실수의 기회를 완전히 없애지는 못한다. 우리는 실수를 할 수 있고 할 것이므로 그것을 수용해야 한다. 중요한 것은 어떻게 그러한 실수의 비용을 최대한 경감할지 이해하는 것이다.

예를 들어 어떤 호출이 발생하고, 이상한 순환참조를 보이고, 지나치게 수다스러운 두 서비스를 발견한다는 그들은 하나로 합쳐져야 할 징조이다. 여기서 사용될 훌륭한 기술은 객체지향시스템의 디장인을 위해 배웠던 접근 방법인 CRC 카드다. CRC 카드로 색인 카드에 클래스의 이름, 그것의 책임, 누구와 협업하는지 기입한다. 제안된 디자인을 통해 작업할 때 필자는 각각의 서비스에 대해 그것이 제공하는 기능 측면에서의 책임과 다이어크램에 지정된 협업자를 나열한다. 더 많은 사용 사례를 통해 작업할 때 이 모든 것이 적절히 들어맞는지 감을 잡을 수 있을 것이다.

5.21 원인 파악

우리는 시스템의 아키텍처가 시간이 지남에 따라 점진적인 방식으로 변화하는 것을 원한다. 따라서 변경비용이 아주 높아지기 전에 분리하는 작업이 필요하다는 사실을 인식하는 것이 핵심이다.

5.22 마치며

우리는 드러난 서비스 경계에 따라접합부를 찾아서 시스템을 점진적인 방법으로 분해한다. 이들 접합부의 발견에 능숙해지고 처음부터 서비스 분리의 비용을 줄이도록 작업하면서 어떠한 신규 요구사항도 만족하도록 시스템을 계속 성장시키고 진화 시킬 수 있다.

이제 서비스를 완전히 분리할 수 잇지만몇가지 새로운 문제에 직면했다.지금 우리에게는 운영환경에 들어갈 많은 구성요소가 있다. 그러므로 다음 장에서는 배포의 세계로 뛰어들 것이다.

Chapter 10 콘웨이의 법칙과 시스템 설계