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

접합부가 중요하다

모놀리스의 단점

  • 응집력을 지향하며 함꼐 변경될 가능성이 높은 것들을 함께 두기 보다는 관련 없는 모든 종류의 코드를 가져와 서로 붙여놓는다.
  • 느슨한 결합은 아예 존재하지도 않는다.
  • 코드 한 줄 정도는 아주 쉽게 변경할 수 있겠지만, 반드시 전체 시스템을 재배포 해야 한다.

접합부

  • 코드베이스의 나머지 부분에 영향을 주지 않는 격리된 코드 부분
  • 경계가 있는 콘텍스트는 훌륭한 접합부를 만든다.
    • 경계가 있는 콘텍스트의 정의는 조직 내의 응집력 있고 느슨히 결합된 경계를 잘 표현한다.

뮤직코퍼레이션 분해하기

뮤직코퍼레이션 온라인 시스템의 아주 많은 행동양식을 구성하는 거대한 모놀리식 서비스 백엔드가 있다고 가정하자.

모놀리식 백엔드가 포함하고 있다고 생각되는 4개의 콘텍스트를 먼저 파악했다.

  • 제품 목록
    • 판매하는 제품에 대한 모든 메타데이터
  • 재무
    • 계정, 결제, 환불 등의 보고
  • 창고
    • 고객 주문 발송 및 반품, 재고 수준 관리 등
  • 추천
    • 특허 출원 중이며 획기적인 우리의 추천 시스템으로 일반 연구소보다 박사 비율이 높은 팀이 매우 복잡한 코드로 작성함
  • 처음 해야 할 일: 콘텍스트들을 대표하는 패키지를 생성하고 그다음에 기존 코드를 패키지로 이동시킨다.
  • 시간이 지나면서 우리는 잘 어울려 남아 있는 코드와 그렇지 못한 코드를 알아가기 시작한다.
  • 끝까지 생존한 코드가 우리가 간과했을지 모를 경계가 있는 콘텍스트로 인식될 수 있다.
  • 위의 과정에서 우리는 패키지 간의 의존성을 분석할 수 있는 코드를 사용할 수 있다.
  • 우리가 만든 코드는 우리 조직을 대표해야 마땅하므로 경계가 있는 콘텍스트를 대표하는 패키지들은 실세계에서 도메인과 같은 방식으로 상호작용해야 한다.
    • 예) 스트럭처 101과 같은 도구는 패키지 간의 의존성을 시각적으로 표현한다.
  • 한번에 모든 것을 바꾸려는 빅뱅 접근법을 취할 필요는 없고, 이과정을 천천히 조금씩 진행되는 것으로 인식해야 한다. 그리고 이 분해 과정을 추적할 수 있는 많은 도구가 있다.

모놀리스는 분리하는 이유

시스템을 조금씩 깎아내듯이 분리하는 점진적 접근법을 통해 마이크로서비스를 더 잘 배울 수 있으며, 무언가 잘못된 경우 피해를 줄일 수 있다.

그럼 어디서부터 시작해야 할까? 코드베이스를 분리했을 때 가장 큰 혜택이 있는 부분을 생각하는 것이 최선이다.

변경의 속도

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

팀 구조

  • 뮤직코퍼레이션의 제품 출시 팀은 두 지역으로 나뉘어, 각각 런던과 하와이에 있다.
  • 만약 하와이 팀이 주로 작업하는 코드를 분리해서 모든 소유권을 가질 수 있다면 정말 좋을 것이다.

보안

  • 뮤직코퍼레이션은 보안 감사를 받았고, 민감한 정보의 보호를 강화하기로 결정했다.
  • 현재는 재무 관련 코드에 대해 진행하고 있다. 만약 이 서비스를 분리한다면 개별 서비스를 모니터링할 수 있고 데이터의 전송과 저장에 있어 추가적인 보호를 할 수 있다.

기술

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

뒤엉킨 의존성

  • 분리할 접합부 몇 개를 인식했을 때 그 코드가 시스템의 나머지 부분과 어떻게 엉켜져 있는가를 고려해야 한다.
  • 우리는 가능한 한 종속성이 낮은 접합부를 추출하기를 원한다.
  • 뒤엉킨 모든 의존성의 출처는 대개 데이터베이스이다.

데이터베이스

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

문제에 대처하기

  • 첫 번째 단계는 코드 자체를 살펴보고, 데이터베이스에 읽고 쓰는 코드 부분을 보는 것이다.
  • 경계가 있는 컨텍스트에 맞게 저장소 계층을 여러 부분으로 분리한다.
  • 특정 콘텍스트 코드 내에 데이터베이스 매핑 코드를 함께 배치하면 어떤 코드가 데이터베이스의 어느 부분을 사용하는지 이해할 수 있다.
    • 경계가 있는 콘텍스트 단위의 매핑 파일과 같은 것을 사용한다면 하이버네이트에서는 매우 명확할 것이다.
  • 외부 키 같은 데이터베이스 수준의 제약을 보기 위해 이 데이터를 시각화하기 위한 다른 도구를 사용해야 한다. (예: 스키마스파이)
  • 결합을 어떻게 끊을 것인가, 같은 테이블이 다수의 경계가 다른 콘텍스트에 사용되는 경우에는 어떻게 할 것인가.
    • 쉽지는 않겠지만 많은 해결책이 있다.

예: 외부 키 관계 깨뜨리기

  • 제품 목록 코드는 앨범 정보를 저장하기 위해 일반적인 행 항목 테이블을 사용하고, 재무 코드는 금융 거래를 기록하기 위해 원장 테이블을 사용한다.
  • 재무 코드가 행 항목 테이블에 접근하는 방법보다는 재무 코드가 호출할 수 있는 제품 목록 패키지의 API를 통해 데이터를 노출한다.
  • 성능에 대한 우려
    • 얼마나 빠른 시스템이 필요한가?, 지금은 얼마나 빠른가? 생각해보자.
  • 주문 서비스에 제품 목록에 대한 ID 리스트가 있을 경우 제품 목록 중 하나가 삭제되어 주문 서비스에서 유효하지 않은 제품 ID를 참조하고 있다면 어떻게 될까?
    • 그것을 허용해야 할까? 허용한다면 어떻게 표시해야 할까? 허용하지 않는다면 위반하지 않은 것을 어떻게 확인할 수 있을까?
    • 이러한 질문은 사용자에 대한 시스템의 행동 방식을 정의하는 사람들을 통해 답을 구해야 할 것이다.

예: 공유 정적 데이터

  • 데이터베이스에 저장된 공유 정적 데이터늬 사례는 매우 많이 볼 수 있다.
  • 향후에 뮤직 쇼핑몰의 모든 서비스가 이런 동일한 테이블을 참조하게 된다면 어떻게 해야 할까?
  • 대안 1. 이 테이블을 각 패키지에 복제한다.
    • 일관성 문제를 초래한다.
  • 대안 2. 공유 정적 데이터를 코드로 다룬다.
    • 아마도 서비스의 부분으로 배포되는 속성 파일에 저장되거나 열거형 개체가 될 수 있다.
      • 데이터베이스 테이블을 변경하는 것보다 설정 파일을 변경하는 것이 훨씬 용이하겠지만, 데이터 일관성에 대한 문제는 여전히 존재한다.
  • 대안 3. 이 정적 데이터를 특정 독립적인 서비스 안에 삽입한다.
  • 대부분의 상황에서 단순한 방법이기 때문에 설정 파일이나 코드에 직접 삽입하는 방식을 시도하고 싶다.(저자 왈)

예: 공유 데이터

  • 변경 가능한 공유 데이터는 시스템을 분리하려 할 때 자주 발생하는 문제가 될 수 있다.
  • 재무 코드는 고객의 주문 결제를 추적하고, 반품할 때 환불 또한 추적한다. 창고 코드는 고객 주문이 발송되고 수취되는 것을 보여주기 위해서 테이블 레코드를 갱신한다. 이 모든 데이터는 웹사이트 상에 보여지고 고객은 그들의 계정으로 어떤 일이 진행되는지 볼 수 있다.
  • 재무와 창고 코드는 동일한 테이블을 읽고 쓰게 된다고 할 때, 이것을 어떻게 분리할 수 있을까?
  • 위에서 누락된 도메인 개념(코드에서 모델링되지 않고 데이터베이스에서 암묵적으로 모델링되는)은 Customer다.
  • 우리는 추상화된 고객 개념을 생성해야 한다.
    • 임시 단계로, Customer로 명명된 새로운 패키지를 만들고 Customer 코드를 재무 또는 창고와 같은 다른 패키지에 노출시키기 위해 API를 사용할 수 있다.
    • 그러면 결과적으로 별도의 고객 서비스가 만들어진다.

예: 공유 테이블

  • 제품 목록은 우리가 판매하는 음반의 이름과 가격을 저장해야 하고, 창고는 재고 음반 목록을 유지해야 한다.
  • 이 두 가지를 일반적인 행 항목 테이블의 동일한 장소에 저장하기로 결정하자.
  • 모든 코드를 병합하기 전에는 관심사들이 실제로 통합되었는지 명확하지 않다.
  • 이에 대한 해결책은 테이블을 분리하는 것이다.
  • 창고를 위해서는 재고 목록 테이블을, 제품 목록 세부 정보를 위해서는 제품 목록 항목 테이블을 생성할 수 있다.

데이터베이스 리팩토링

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

단계적인 분리

  • 각각의 마이크로서비스로 만들기 전에, 서비스는 이전과 같이 하나로 유지한 채 우선 스키마를 분리할 것을 추천하고 싶다.
  • 스키마가 분리되면 단일 작업을 수행하는 데이터베이스의 호출 횟수가 증가할 수 있다.
    • 두 개의 테이블에서 추출해서 메모리상에서 조인을 수행해야 한다.
  • 두 개의 스키마로 분리하면 결국 트랜잭션 일관성을 깨뜨리게 된다.
  • 애플리케이션 코드를 그대로 유지한 채 스키마만 분리하면 변경한 것을 되돌리거나 서비스의 소비자에게 영향을 주지 않고 작은 변경을 계속할 수 있다.

트랜잭션의 경계

트랜잭션은 이벤트가 모두 발생하거나 전혀 발생하지 않게 해준다.

트랜잭션을 데이터베이스 분야에서만 적용되는 것은 아니다. 메시지 브로커는 오래 전부터 트랜잭션 내에서 메시지를 게시하고 수신해왔다.

스키마를 두 개의 스키마로 분리하면, 즉 고객과 관련된 데이터를 주문 테이블과 창고 테이블로 나누면 트랜잭션 안정성을 잃게 된다.

나중에 재시도하기

  • 주문을 받고 처리했다는 사실은 나중에 창고의 수집 테이블에 삽입을 재시도할 수 있다.
    • 이 연산의 일부를 큐나 로그 파일에 큐잉하여 나중에 재시도 할 수 있다.
  • 최종적 일관성의 또 다른 형태로서 트랜잭션이 완료되었을 때 시스템이 일관성을 유지하는 상태임을 보장하기 위해 트랜잭션 경계를 사용하는 대신 향후 특정 시점에 시스템이 스스로 일관성을 유지하는 상태가 될 수 있음을 허용한다.
  • 비즈니스 작업들이 오래 지속될 경우 특히 유용하다.

전체 작업 중지하기

  • 시스템을 다시 일관성이 유지된 상태로 복귀시켜야 한다.
  • 수집 테이블에 삽입이 실패할 경우 그 테이블은 쉽게 되돌려지지만 주문 테이블에는 되돌려야 할 트랜잭션이 커밋되어 있다.
  • 우리가 해야 할 일은 보상 트랜잭션을 발행하는 것으로, 직전의 트랜잭션을 되돌릴 새로운 트랜잭션을 발생시키는 것이다.
    • 주문을 삭제하기 위해 DELETE 문을 발행하는 것처럼 단순할 수도 있다.
      • 그러고 나서 작업이 실패했다는 UI를 통해 리포트할 필요가 있다.
    • 보상 트랜잭션을 처리하는 로직은 고객 서비스, 주문 서비스 또는 다른 어떤 서비스에 존재해야 할까?
  • 보상 트랜잭션이 실패하면?
    • 수집 명령과 일치되지 않는 주문이 주문 테이블에 있게 된다.
    • 보상 트랜잭션을 재시도하거나 일관성이 유지되지 않는 것을 정리하는 백엔드 프로세스를 수행할 필요가 있다.
    • 이는 관리자가 접근할 수 있는 관리 스크린 또는 자동화된 프로세스처럼 단순할 수 있다.
  • 일관성이 유지되기 바라는 작업이 한두 개가 아니라 더 많다면 어떤 일이 벌어질까?
    • 각각의 실패 모드에 대해 보상 트랜잭션을 처리하는 것은 구현은 고사하고 이해하기도 매우 어렵다.

분산 트랜잭션

  • 보상 트랜잭션을 수동으로 통제하는 방식의 대안은 분산 트랜잭션의 사용이다.
  • 분산 트랜잭션은 하부 시스템에서 수행되는 다양한 트랜잭션은 통제하기 위해 트랜잭션 관리자라는 전체적인 통제 프로세스를 사용해서 트랜잭션 내부의 여러 트랜잭션을 확장하려 시도한다.
  • 분산 트랜잭션도 일관된 상태를 유지하려고 노력한다. 그리고 일관된 상태가 유지되는 경우에만 여러 다른 시스템의 다른 프로세스에서 실행되도록 한다. 이 경우 종종 네트워크 경계를 넘어 통신한다.
  • 2단계 커밋
    • 분산 트랜잭션을 처리하는 가장 범용적인 알고리즘이다.
      • 진행 순서:
        • 투표 단계가 시작된다.
        • 이 단계에서 각 참여자(코호트라고도 함)는 로컬 트랙잭션의 진행 가능 여부를 트랜잭션 매니저에 알린다.
        • 만약 트랜잭션 매니저가 모든 참여자로부터 찬성표를 얻는다면 참여자에게 각자의 커밋을 수행하라고 지시한다.
        • 반대표를 하나라도 받는다면 모든 참여자에게 롤백 명령을 보낸다.
    • 이 방법에서는 중앙의 조정 프로세스가 진행을 허락할 때까지 모든 참여자는 중지해야 하는데 이는 장애에 취약함을 의미한다.
      • 트랜잭션 매니저가 다운되면 대기하고 있던 트랜잭션은 결코 수행되지 않는다.
      • 코호트가 투표중에 응답하지 못하면 모든 것이 차단된다.
    • 투표 후에 커밋이 실패하는 경우도 있다.
    • 이 알고리즘은 이러한 일이 발생할 수 없다는 암묵적인 가정을 한다.
      • 이 알고리즘이 완벽한 것이라고 하기 보다는 대다수의 실패 사례를 자방내기 위한 노력임을 의미한다.
    • 이러한 조정 과정은 잠금(lock)을 의미하기도 한다. 즉, 대기중인 트랜잭션은 자원을 잠금 상태로 유지할 수 있다. 자원의 잠금은 특히 분산 시스템에서 시스템의 확장을 더 어렵게 만들고 자원에 대한 경쟁을 초래할 수 있다.
  • 분산 트랜잭션은 자바 트랜잭션 API와 같은 특정 기술 스택으로 구현되어 왔으며 데이터베이스와 메시지 큐처럼 서로 상이한 자원을 모든 참여자가 동일하며 중요한 트랜잭션으로 사용할 수 있게 했다.

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

  • 분산 트랜잭션을 제대로 수행하는 것은 쉽지 않으며 실제로 확장성을 저해할 수 있다.
  • 재시도 보상 로직을 최종 방법으로 사용하는 시스템들은 추론하기 더 어려우며 데이터의 불일치를 고치기 위해 다른 보상 행위가 필요할 수 있다.
  • 단일 트랜잭션 내에서 발생하는 비즈니스 연산을 접하게 되면 그것이 정말로 필요한지 자문해보라.
    • 비즈니스 연산이 서로 다른 지역의 트랜잭션에서 발생하고 최종적인 일관성의 개념에 의존할 수 있는가? 이러한 시스템은 구축하고 확장하기 훨씬 용이하다. (11장)
  • 정말로 일관성이 유지되어야 하는 상태가 필요하다면 처음부터 그 상태가 유지되도록 할 수 있는 모든 것을 하라.
  • 불가피하게 분리는 감수해야 한다면 단지 그 과정(트랜잭션)의 기술적 관점에서 벗어나 트랜잭션 자체를 표현할 구체적인 개념을 만들어라. 이것은 보상 트랜잭션과 같은 다른 작업을 실행할 여지를 주고 시스템의 복잡한 개념들을 모니터링하고 관리할 수 있는 방법을 제공한다.
    • 주문을 처음부터 끝까지 처리하는(그리고 예외를 다루는) 모든 로직을 한데 모으기에 적합한 '진행 중인 주문'과 같은 개념을 만들 수 있다.

리포팅

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

리포팅 데이터베이스

  • 리포팅은 유용한 결과를 생성하기 위해 조직 내 여러 부분의 데이터를 함께 모을 필요가 있다.
    • 제품 목록과 판매 설명이 있는 일반적인 원장에 더 풍부한 데이터를 추가하거나 특정 VIP 고객들의 정보와 구매 내역을 포함한 구매 행동 패턴을 보고 싶을 것이다.
  • 일반적인 모놀리식 서비스 아키텍처에서는 모든 데이터가 거대한 단일 데이터베이스에 저장되기 때문에 리포팅하는 것이 매우 쉽다.
  • 일반적으로 리포팅 시스템은 리포팅 질의가 메인 시스템의 성능에 영향을 주는 것을 우려하여 리포트를 메인 데이터베이스에서 실행하지 않고 읽기용 복제 데이터베이스에 연결한다.
    • 단점 1: 스키마의 변경이 조심스럽게 관리되어야 한다. 이는 스키마를 변경하고 조율할 기회를 줄이는 방해물이다.
    • 단점 2: 실제 시스템 또는 리포팅 시스템을 지원하는 사용 사례를 위해 데이터베이스를 최적화하는 방법이 제한적이다.
    • 단점 3: 데이터베이스 방법이 굉장히 많아졌다.
      • Neo4j: 그래프로 모델링하는 것이 낫다면 / MongoDB: 문서 저장소 사용 / 카산드라: 대용량 확장이 매우 용이한 리포팅 시스템을 위해
  • 우리 정보가 서로 다른 시스템에 저장되어 있다면 리포트를 생성하기 위해 모든 데이터를 어떻게 한 곳에 모을까?
  • 일반적인 리포팅 데이터베이스 모델과 연관된 몇몇 단점을 제거할 방법을 찾을 수 있을까?

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

  • 마이크로서비스에 의해 노출되는 API는 리포팅 용도로 설계된 것이 아닐 가능성이 크다.
    • 고객 서비스는 ID로 고객을 검색할 수 있게 허용하지만, 모든 고객을 추출하는 API를 반드시 외부에 공개하는 것은 아니다.
  • 서비스에 의해 노출되는 자원에 캐시 헤더를 추가하여 일부 데이터의 추출 속도를 높이고 리버스 프록시와 같은 장소에 이 데이터를 새키할 수 있지만, 리포팅은 흔히 장기간의 데이터에 접근하는 특징이 있다.
    • 잠재적으로 연산 비용이 높은 캐시 미스가 발생한다.
  • 리포팅을 쉽게 만들기 위해서는 배치 API를 제공해서 이 문제를 해결할 수 있다.
    • 고객 서비스는 배치를 통해 고객 ID 리스트를 전달받아 고객 정보를 추출하거나 모든 고객 정보를 페이지 단위로 제공하는 인터페이스를 노출할 수 있다.
    • 더 극단적인 방법은 배치 요청을 자원으로 자체 모델링하는 것이다.
    • 호출하는 시스템은 BatchRequest를 POST할 수 있다.
    • 아마도 모든 데이터가 담긴 파일의 위치도 함께 전달할 수 있다.
      • 고객 서비스는 요청이 접수되었지만 아직 처리되지 않았음을 나타내는 HTTP 202 응답 코드를 되돌려준다.
      • 호출 시스템은 자원이 생성되었음을 명시하는 201 응답 코드를 받기 전까지 폴링할 수 있고, 응답을 받은 후 데이터를 가져올 수 있다.
      • 이는 시스템이 CSV 파일을 공유 위치에 저장하는 것과 같은 방식으로 대용량 데이터 파일을ㅇ HTTP 상에 부하 없이 내보낼 수 있다는 것을 의미한다.

데이터 펌프

리포팅 시스템이 데이터를 끌어오는 방식 대신 리포팅 시스템에 데이터를 밀어 넣는 방식을 시도할 수 있다.

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

지금까지 동일한 데이터베이스에 많은 프로그램을 통합하는 것은 매우 나쁘다고 했었다. 하지만 적절하게 구현되었다면 이 방식을 통해 리포팅을 쉽게 생성하며 결합으로 인한 단점을 크게 완화할 수 있는 주목할 만한 예외다.

  • 데이터 펌프를 시작하려면 해당 서비스를 관리하는 팀이 그것을 개발하고 관리해야 한다.
  • 서비스와 데이터 펌프 중 하나가 배포될 때 같이 배포할 것을 가정해 버전을 함께 관리하고, 데이터 펌프를 서비스 자체 빌드의 일부로 추가해 부차 결과물로 생성할 것을 제안한다.
    • 함께 배포하고 서비스 팀 외부의 누구에게도 스키마의 접근을 개방하지 않기 때문에 전통적인 DB 통합 문제의 많은 부분이 크게 완화된다.
  • 리포팅 스키마 자체의 결합 문제가 남아 있지만 이것은 변경하기 어려운 게시된 API처럼 다뤄야 한다.

대체 종착지

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

이벤트 데이터 펌프

고객 서비스는 특정 고객이 생성, 수정 또는 삭제될때 이벤트를 발생할 수 있다.

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

이제 고객 서비스와 같은 원본 마이크로서비스의 하부 데이터베이스와 결합되지 않는다. 대신 해당 서비스에 의해 발산된 이벤트와 바인딩한다.

  • 이벤트가 발생할 때 리포팅 시스템에 데이터를 보낼 수 있으므로 정기적으로 데이터를 밀어 넣는 것보다 더 신속하게 데이터가 리포팅 시스템에 흘러들어갈 수 있다.
  • 또한 이미 처리된 이벤트를 저장한다면 오래된 이벤트가 이미 리포팅 시스템에 매핑되었다고 가정하고 새로운 이벤트가 도착할 때 그것만 처리하면 된다. 변경된 내용만 리포팅 시스템에 보내기 때문에 데이터를 더 효율적으로 추가할 수 있다.
  • 이 방법의 주요 단점은 필요한 모든 정보를 이벤트로 확산해야 하는 것이다.
    • 확장뿐만 아니라 대용량의 데이터를 위한 데이터 펌프도 불가능할 수 있다.
    • 그럼에도 불구하고 이미 적절한 이벤트를 공개하고 있다면 고려해볼 만하다.

백업 데이터 펌프

이 방안은 넷플릭스에서 사용되며 기존의 백업 솔루션을 확용하여 그들이 겪은 몇 가지 확장성 문제도 해결한다.

  • 넷플릭스는 많은 서비스를 위한 자료 저장소로 카산드라를 표준화하기로 결정했다.
  • 카산드라 데이터를 백업하기 위한 일반적인 접근법은 데이터 파일을 복사해서 안전한 곳에 저장하는 것이다.
  • 넷플릭스는 SSTable로 알려져 있는 이런 파일들을 상당한 수준의 데이터 내구성을 자랑하는 AWS의 S3 객체 저장소에 저장한다.
  • 넷플릭스는 이 모든 데이터를 리포트해야 하는데 이것은 엄청난 리포팅 규모이다.
  • 그들은 SSTable에 백업된 것을 작업 원본으로 사용하는 하둡을 통해 방대한 양의 데이터를 처리할 수 있는 파이프라인을 구현했으며 나중에 이것은 아이기스토스 프로젝트로 오픈 소스화되었다.
    • 하지만 데이터 펌프처럼 이 백업 데이터 펌프 패턴 역시 리포팅 스키마(또는 타깃 시스템)와 결합되어 있다.

실시간을 향해

대시보드, 알림, 재무 리포트, 사용자 분석 등과 같은 사용 사례는 정확성과 타임라인에 대한 각기 다른 허용 오차를 가지며, 다른 기술적인 선택을 하게 될 것이다.

우리는 필요에 따라 데이터를 다양한 장소로 라우팅할 수 있는 포괄적인 이벤트 시스템을 향해 더욱더 전진하고 있다. (8장에서 상세히 다룸)

변경 비용

우리가 제안하는 디자인을 스케치하고 그 사용 사례들을 자신이 생각하는 서비스 경계를 넘어 실행할 때 어떤 일이 발생할지 보아라.

  • 예를 들어 뮤직 쇼핑몰에서 고객이 레코드를 검색하거나, 웹사이트에 가입하거나, 앨범을 구매할 때 어떤 일이 발생할 지 상상해보라.
  • 어떤 호출이 발생하는가? 이상한 순환 참조를 볼 수 있는가? 지나치게 호출이 많은 두 서비스를 발견했는가? 그들은 하나로 합쳐져야 할 징조일 수 있다.

여기에 사용될 훌륭한 기술은 객체 지향 시스템의 디자인을 위해 배웠던 접근 방법인 CRC 카드다.

  • CRC 카드로 색인 카드에 클래스의 이름, 그것의 책임, 누구와 협업하는지 기입한다.
  • 제안된 디자인을 통해 작업할 때 각각의 서비스에 대해 그것이 제공하는 기능 측면에서의 책임과 다이어그램에 지정된 협업자를 나열하라.

원인 파악

서비스 분리와 연관된 비용도 중요하다. 서비스를 실행시킬 곳을 찾고 새로운 서비스 스택을 시작하는 것은 사소한 작업이 아니다.

어떤 일을 제대로 하는 것이 어렵다면 그것을 더 쉽게 할 수 있도록 노력해야 한다.

라이브러리와 경량 서비스 프레임워크에 대한 투자는 새로운 서비스를 생성하는 데 드는 비용을 줄일 수 있다. 사람들에게 직접 프로비전할 수 있는 가상 머신에 대한 접근을 허용하거나 PaaS 생성까지도 허용하는 것은 시스템을 쉽게 프로비전하고 테스트하기 쉽게 만들 것이다.

마치며

  • 우리는 드러난 서비스 경계에 따라 접합부를 찾아서 시스템을 점진적인 방법으로 분해한다.
  • 이 접합부의 발견에 능숙해지고 처음부터 서비스 분리의 비용을 줄이도록 작업하면서 어떠한 신규 요구 사항도 만족하도록 시스템을 계속 성장시키고 진화시킬 수 있다.
  • 이러한 작업의 일부는 고단한 일일 수 있으나 그 작업이 점진적으로 수행될 수 있다는 사실은 그것을 두려워할 필요가 없음을 뜻한다.

이제 서비스를 완전히 분리할 수 있지만 몇 가지 새로운 문제가 있다.

실제 운영 환경에는 들어가야 할 많은 구성 요소가 있다. 다음 장에서 알아보자.

+ 따끈한 최근 게시물