Nguyen Le Phong

소프트웨어 아키텍처 기초6부 중 5부

모놀리스, Modular Monolith, 마이크로서비스: 솔직한 의사결정 가이드

마이크로서비스는 출발점이 아니라 조직 규모에 대해 지불하는 세금입니다. 모놀리스에서 Modular Monolith, 마이크로서비스까지의 과장 없는 여정 — 언제(그리고 분리할지 여부를) 알려주는 신호와 함께.

더 자주 일어나야 하는 것보다 더 많이 일어나는 장면이 있습니다: 3인 스타트업이 첫날 마이크로서비스를 구축하기로 결정합니다. 6개월 후 그들은 열다섯 개의 서비스, 아무도 완전히 이해하지 못하는 Kafka 클러스터, 그리고 재현하려면 여덟 개의 서비스를 거쳐 요청을 추적해야 하는 버그를 가지고 있습니다. 팀은 지쳐있습니다. 제품은 반만 완성되어 있습니다. 그리고 Slack 스레드 어딘가에서 누군가가 우리는 기본적으로 분산 모놀리스를 구축했습니다라는 말을 타이핑합니다.

마이크로서비스는 진정으로 강력합니다 — 올바른 규모에서, 올바른 팀과 함께, 올바른 이유로. 하지만 그것들은 시작점이 아니라 조직 규모에 지불하는 세금입니다. 이 가이드는 세 가지 아키텍처 모양 모두를 솔직하게 살펴봅니다 — 모놀리스, Modular Monolith, 그리고 마이크로서비스 — 그리고 언제(그리고 여부를) 이동해야 하는지를 알 수 있는 신호를 제공합니다.

모놀리스: 모든 것을 지배하는 하나의 배포 단위

모놀리스는 단일 배포 단위입니다. 하나의 빌드, 하나의 바이너리(또는 하나의 컴파일된 자산 세트), 하나의 배포 단계. 모든 기능, 모든 모듈, 모든 데이터베이스 쿼리가 같은 프로세스에 삽니다.

그것은 제한적으로 들리고, 단어 자체가 약간 부끄러운 뉘앙스를 갖게 되었습니다 — 마치 모놀리스가 있다고 인정하는 것이 현대 엔지니어링을 따라가지 못했다고 인정하는 것처럼. 그 프레임은 잘못되었고, 많은 불필요한 고통을 야기했습니다.

모놀리스가 올바른 기본값입니다. 이유는 다음과 같습니다:

  • 로컬 호출은 무료입니다. 프로세스 내의 함수 호출은 나노초입니다. 서비스 간 네트워크 호출은 밀리초이고 — 실패하거나, 타임아웃되거나, 부분적인 결과를 반환하거나, 완전히 손실될 수 있습니다. 그 복잡성은 필요할 때까지 짊어지지 않아도 됩니다.
  • 트랜잭션이 쉽습니다. 관계형 데이터베이스는 전체 데이터 모델에 걸쳐 ACID 트랜잭션을 제공합니다. 모놀리스에서 계정 A에서 계정 B로 돈을 이동하는 것은 하나의 트랜잭션입니다. 마이크로서비스에서는 분산 트랜잭션 또는 사가가 됩니다 — 진짜 실패 모드가 있는 진정으로 어려운 문제들.
  • 디버깅이 간단합니다. 하나의 프로세스, 하나의 로그 스트림, 하나의 스택 추적. 문제를 찾습니다. 분산 시스템에서는 단일 사용자 액션이 여러 서비스에 걸쳐 수십 개의 비동기 작업을 생성할 수 있으며, 그것들을 연관짓는 것은 목적에 맞게 구축된 관측 도구가 필요합니다.
  • 배포가 단순합니다. 하나의 아티팩트, 하나의 파이프라인, 하나의 롤백 전략. 첫날부터 자신 있게 출시할 수 있습니다.
  • 대기업들이 큰 모놀리스를 출시합니다. Shopify는 거대한 규모에서 수년간 Rails 모놀리스를 실행했습니다. Stack Overflow는 모놀리스를 실행하는 몇 개의 서버에서 하루에 수백만 개의 요청을 처리합니다. 모놀리스는 확장을 막지 않습니다 — 단지 다르게 확장할 뿐입니다.

모놀리스의 실제 고통은 실제이지만, 사람들이 예상하는 것보다 늦게 옵니다. 빌드 시간은 코드베이스가 커지면서 증가합니다 — 50k 줄에서 2분 걸리는 전체 재빌드가 500k에서는 12분 걸립니다. 병합 마찰은 스무 명의 엔지니어가 모두 같은 코드베이스를 수정할 때 증가합니다; 오래 지속되는 브랜치와 충돌이 일상적인 의식이 됩니다. 배포 결합은 모든 팀이 함께 배포한다는 것을 의미합니다 — 한 팀의 위험한 변경이 다른 모든 사람의 릴리즈를 막을 수 있습니다.

이 고통들은 진짜입니다. 하지만 그것들은 성공의 고통이며, 과대광고가 시사하는 것보다 훨씬 늦게 옵니다. 대부분의 팀들은 그것들을 맞닥뜨릴 만큼 운이 좋아야 합니다.

Modular Monolith: 대부분의 팀이 건너뛰는 최적 지점

Modular Monolith는 여전히 하나의 배포 단위이지만 — 내부적으로는 강제된 경계를 가진 모듈로 구성됩니다. 모듈들은 명확한 인터페이스를 통해 소통합니다. 하나의 모듈이 다른 모듈의 내부에 직접 접근하는 것은 허용되지 않습니다: 형제 모듈에서 비공개 클래스를 import하지도, 데이터베이스 테이블을 공유하지도, 모듈 경계를 넘어 내부 헬퍼 함수를 호출하지도 않습니다.

이것은 작은 훈련처럼 들리지만, 게임을 크게 바꿉니다.

집의 평면도처럼 생각해 보세요. 모듈 없는 모놀리스는 원룸: 모든 것이 어디에나 있는 하나의 큰 열린 공간. Modular Monolith는 벽이 있는 방들을 제공합니다 — 여전히 한 집에 살고, 여전히 배관과 현관을 공유하지만, 부엌과 침실은 각자 뚜렷한 목적을 가지고 침대에서 요리하지 않습니다.

모듈들은 도메인 주도 설계의 경계 컨텍스트(bounded context)에 자연스럽게 매핑됩니다: Billing 모듈, Inventory 모듈, Notifications 모듈. 각각은 자체 도메인 로직과 자체 데이터베이스 스키마 슬라이스를 소유합니다. 그들은 공개 인터페이스(서비스 파사드 또는 공개 함수 세트)를 노출하고 다른 모든 것은 비공개로 유지합니다.

실질적인 이득은 상당합니다:

  • 독립적인 추론. Billing을 담당하는 개발자는 전체 코드베이스를 머릿속에 담지 않고도 이해하고 수정할 수 있습니다.
  • 안전한 리팩터링. 공개 인터페이스가 보존되는 한 모듈의 내부를 다시 작성할 수 있습니다 — 앱의 나머지는 알아채지 못합니다.
  • 명확한 소유권. 팀들이 모듈을 클레임합니다. 코드 리뷰가 집중적으로 유지됩니다. 새 개발자를 온보딩하는 것은 200k 줄짜리 코드베이스가 아닌 하나의 모듈을 건네주는 것을 의미합니다.
  • 마이그레이션 준비 이음새. 서비스를 추출해야 한다면, 경계가 이미 그어져 있습니다. 모듈이 서비스가 됩니다. 인터페이스가 API가 됩니다. 작업이 상당하지만, 탐색적 수술이 아닌 알려진 작업입니다.

Modular Monolith는 대부분의 팀들이 뛰어넘어, 얽혀있는 모놀리스에서 여기서 멈추지 않고 마이크로서비스로 달려가는 아키텍처입니다. 그것은 실수입니다. 대다수의 제품 팀 — 100명 미만의 엔지니어, 한 자릿수 서비스, 한두 개의 배포 파이프라인 — 에게는 잘 유지된 Modular Monolith가 살기에 가장 생산적인 곳입니다.

마이크로서비스: 실제로 무엇을 사는가

마이크로서비스는 시스템을 독립적으로 배포 가능한 서비스들로 분해합니다. 각 서비스는 자체 프로세스, 자체 배포 파이프라인, 그리고 — 결정적으로 — 자체 데이터 저장소를 소유합니다. 서비스들은 네트워크를 통해 서로 이야기합니다: HTTP, gRPC, 메시지 큐, 이벤트 스트림.

마이크로서비스가 잘 작동할 때, 규모에서 다른 어떤 것도 필적할 수 없는 네 가지를 제공합니다:

  • 독립적 배포. 결제 팀은 추천 팀, 검색 팀, 또는 알림 팀과 조율하지 않고 화요일 오후에 결제 서비스 변경을 출시할 수 있습니다. 수백 명의 엔지니어가 있는 회사에서, 이것은 변혁적입니다 — 주 1회 배포와 하루 100회 배포의 차이.
  • 독립적 확장. ImageProcessing 서비스가 피크 시간에 40개의 CPU 코어가 필요하고 UserProfile 서비스가 두 개만 필요하다면, 별도로 확장할 수 있습니다. 모놀리스에서는 전체를 확장합니다 — 필요하든 않든 모든 기능에 40개의 코어를 구매합니다.
  • 팀 자율성. 각 서비스는 작고 자기 완결적인 제품입니다. 5명의 엔지니어 팀이 서비스를 완전히 소유할 수 있습니다: 언어, 데이터베이스, 배포 주기를 선택합니다. 이것이 마이크로서비스의 조직적 슈퍼파워입니다 — Conway의 법칙이 여러분에게 불리하게 작동하는 것이 아니라 유리하게 작동합니다.
  • 장애 격리. RecommendationEngine의 메모리 누수는 RecommendationEngine을 다운시키지, 체크아웃을 다운시키지 않습니다. 우아하게 저하될 수 있습니다 — 스토어는 여전히 작동하고, 제품에 개인화된 추천이 없을 뿐입니다. 모놀리스에서는 어느 모듈의 잡히지 않은 예외가 전체 프로세스를 다운시킬 수 있습니다.

패턴에 주목하세요: 그 목록의 모든 것이 조직적이고 운영적인 규모에 관한 것입니다. 이것들은 아직 제품-시장 적합성을 찾지 못한 8명의 사람들이 제품을 구축하는 팀에는 적용되는 관심사가 아닙니다.

실제로 지불하는 것: 분산 시스템 세금

마이크로서비스는 무료가 아닙니다. 위의 모든 혜택에는 대응하는 비용이 있으며, 그 비용은 사소하지 않습니다. 그것들을 과소평가하는 것이 팀들이 지치고 미완성으로 끝나는 방법입니다.

분산 모놀리스 경고

최악의 결과는 마이크로서비스나 모놀리스가 아닙니다 — 분산 모놀리스입니다: 함께 배포해야 할 만큼 단단히 결합되고, 같은 데이터베이스를 공유하거나, 함께 실패하는 마이크로서비스. 마이크로서비스의 모든 비용을 지불하고 혜택은 하나도 받지 못합니다. 팀들이 비즈니스 도메인 대신 기술 레이어로 분리하거나, 서비스들이 긴 체인으로 동기적으로 서로를 호출할 때 일어납니다. ServiceAServiceB 이후에만 배포될 수 있다면, 분산 모놀리스를 구축한 것입니다.

전체 세금은 다음과 같습니다:

  • 네트워크 호출은 실패합니다. 함수 호출은 실패할 수 없습니다(버그를 제외하고). 네트워크 호출은 실패하거나, 타임아웃되거나, 오래된 캐시 값을 반환하거나, 첫 번째 시도가 이미 사이드 이펙트를 가진 후 재시도에서 성공할 수 있습니다. 어디서든 항상 부분 실패를 처리해야 합니다.
  • 결과적 일관성. 서비스들은 자체 데이터를 소유합니다. 서비스 경계를 넘어 그 데이터를 일관되게 유지하는 것은 신중한 설계가 필요합니다 — 이벤트 주도 패턴, 사가, 멱등성 작업, 보상 트랜잭션. 사용자가 주문을 배치했지만 즉시 주문 이력에서 볼 수 없는 이유를 제품 매니저에게 설명하는 것은 어색합니다.
  • 분산 트랜잭션은 어렵습니다. 2단계 커밋 프로토콜이 존재하고 비참합니다. 대부분의 팀들은 대신 사가 패턴을 사용하는데, 이는 복잡성을 데이터베이스에서 애플리케이션 코드로 옮깁니다. 둘 다 BEGIN; UPDATE; COMMIT;보다 훨씬 복잡합니다.
  • 디버깅이 다른 기술입니다. 스택 추적이 더 이상 무엇이 잘못되었는지 알려주지 않습니다. 분산 추적(Jaeger, Zipkin, Honeycomb), 모든 로그 라인의 상관 ID, 서비스 메쉬, 그리고 서비스 전반에 걸쳐 이벤트를 연관짓는 대시보드가 필요합니다. 이 관측 인프라를 구축하고 유지하는 것은 실제 엔지니어링 작업입니다.
  • 운영 오버헤드가 곱해집니다. 하나의 서비스에는 Dockerfile, Kubernetes 배포, 인그레스 규칙, 헬스 체크 엔드포인트, CPU와 메모리 제한, 로그 집계 구성, 그리고 알림 규칙이 필요합니다. 30개 서비스를 곱하세요. 이 오버헤드는 서비스 수에 선형적으로 확장됩니다; 엔지니어링 인원은 그렇지 않습니다.
  • 테스트가 더 어렵습니다. 유닛 테스트는 여전히 쉽습니다. 하지만 서비스 경계를 넘는 통합 테스트는 여러 서비스를 동시에 실행하고, 버전과 구성을 관리하고, 데이터베이스 전반의 테스트 데이터를 다루는 것이 필요합니다. 계약 테스트(Pact 등)가 도움이 되지만 배울 자체 훈련을 추가합니다.

이 비용들은 해결 불가능한 것이 아닙니다 — 산업에는 모두를 위한 성숙한 도구가 있습니다. 요점은 그것들이 매일 지불해야 하는 실제 비용이며, 그것들이 잠금 해제하는 혜택이 실제로 필요할 때까지 지불할 가치가 없다는 것입니다.

신호: 분리할 때(그리고 하지 말아야 할 때)

그렇다면 마이크로서비스가 비용만큼의 가치가 있는 임계값을 넘었다는 것을 어떻게 알 수 있을까요? 실제로 시간임을 나타내는 신호들이 있습니다:

  • 독립적인 확장 요구가 실제이고 비쌉니다. 시스템의 나머지보다 10배의 자원이 필요한 특정 컴포넌트를 지목할 수 있고, 전체 모놀리스에 걸쳐 그 자원에 비용을 지불하고 있습니다. 분리하면 의미 있는 비용을 절약하거나 성능을 의미 있게 향상시킬 것입니다.
  • 배포 경쟁이 만성적입니다. 팀들이 다른 팀의 진행 중인 변경으로 인해 정기적으로 출시가 막힙니다. 공유 배포의 조율 오버헤드가 측정 가능하게 여러분을 늦추고 있습니다 — 가끔이 아니라 주간 또는 일간 불편 사항으로.
  • 팀 수가 그것을 요구합니다. 각자 다른 배포 주기, 다른 기술 선호도, 그리고 진정으로 독립적인 로드맵을 가진 별개의 비즈니스 도메인에서 작업하는 5명 이상의 엔지니어로 구성된 여러 팀이 있습니다. Conway의 법칙은 끝나게 될 아키텍처를 예측합니다 — 그것을 계획하는 것이 좋습니다.
  • 컴포넌트에 근본적으로 다른 신뢰성 또는 보안 요구 사항이 있습니다. 결제, 인증, PII 저장소는 종종 성능이 아닌 컴플라이언스, 영향 범위 감소, 독립적 감사 가능성을 위해 격리를 보장합니다.
  • 모듈이 이미 사실상 별개입니다. 시스템의 나머지와 공유 상태가 없고, 잘 정의된 이벤트나 API를 통해서만 소통하며, 다른 팀이 소유합니다. 조직적 이음새가 이미 존재합니다 — 그것을 서비스 경계로 만드는 것은 이미 사실인 것을 공식화하는 것입니다.

그리고 너무 일찍 분리하고 있다는 신호들:

  • 전체 엔지니어링 조직이 20명 미만입니다.
  • 구체적인 한계에 부딪혔기 때문이 아니라 더 확장 가능하게 느껴지기 때문에 분리하고 있습니다.
  • 계획하는 서비스들이 작동하려면 함께 배포해야 합니다.
  • 각 서비스가 공유 테이블 없이 자체 데이터를 소유하는 깔끔한 경계를 아직 그을 수 없습니다.
  • 팀에 분산 시스템을 디버깅하기 위한 관측 인프라(분산 추적, 중앙 집중 로깅, 알림)가 아직 없습니다.
  • 주된 이유가 마이크로서비스가 직무 설명이나 회사 마케팅 문구에 있기 때문입니다.

잘 분리하는 방법: 이음새, 교살자 무화과, 데이터 소유권

위의 신호들이 분리를 가리킨다면, 방법이 매우 중요합니다. 잘못 분리하면 위에서 설명한 분산 모놀리스가 만들어집니다. 잘 분리하면 스스로의 비용을 지불하는 진정으로 독립적인 서비스가 만들어집니다.

기술 레이어가 아닌 경계 컨텍스트를 따라 분리하세요. DatabaseServiceValidationService를 만들지 마세요 — 그것들은 기술적 관심사이지, 비즈니스적인 것이 아닙니다. OrderService, BillingService, InventoryService를 만드세요 — 도메인에서 명확하고 안정적인 의미를 가진 비즈니스 기능 단위. 경계 컨텍스트는 특정 모델이 적용되고 언어가 일관된 도메인의 부분입니다. 그곳이 서비스 경계가 속하는 곳입니다.

교살자 무화과 패턴을 사용하세요. 호스트 나무를 점진적으로 감싸는 덩굴에서 이름을 딴, 교살자 무화과 패턴은 한꺼번에가 아닌 점진적으로 서비스를 추출할 수 있게 합니다. 모놀리스와 나란히 새 서비스를 세우고, 특정 트래픽 슬라이스를 그것으로 라우팅하고, 작동하는지 확인한 다음, 모놀리스에서 해당 코드를 제거합니다. 모놀리스는 시간이 지남에 따라 줄어듭니다; 절대 재작성되지 않습니다. 이것이 더 안전하고, 더 되돌릴 수 있으며, 결코 완전히 끝나지 않는 6개월짜리 빅뱅 마이그레이션이 될 가능성이 훨씬 낮습니다.

한 번에 하나의 서비스를 추출하세요. 모든 추출이 무언가를 가르쳐 줍니다. 두 번째 추출이 첫 번째보다 더 잘 될 것입니다. 다섯 개의 서비스를 동시에 추출하려 하면 학습이 분산되고 위험이 배가됩니다.

데이터 소유권은 협상 불가능합니다. 다른 서비스와 데이터베이스 테이블을 공유하는 서비스는 서비스가 아닙니다 — 추가 네트워크 오버헤드가 있는 모듈입니다. 각 서비스는 자체 데이터를 소유해야 합니다. 두 서비스가 같은 데이터를 필요로 한다면, 하나가 권위적이고 다른 것은 API를 통해 가져오거나 이벤트를 통해 동기화합니다. 이 제약은 수립하기 어렵고 유지하기 어렵지만, 그것이 원하는 독립적 배포 가능성과 장애 격리를 제공하는 것입니다.

포트를 사용하여 추출 전에 이음새를 정의하세요. Modular Monolith 내부에서 Ports & Adapters 접근법을 따랐다면, 추출이 거의 기계적이 됩니다: 포트가 API를 정의하고, 그 뒤의 어댑터가 서비스 클라이언트가 됩니다. 도메인 로직은 변하지 않습니다 — 딜리버리 메커니즘만 바뀝니다. 이것이 처음부터 포트로 구축하는 가장 강력한 실용적 주장 중 하나입니다: 무료로 추출 준비가 된 이음새를 제공합니다.

나란히 비교

차원 모놀리스 Modular Monolith 마이크로서비스
배포 단위 하나의 프로세스, 하나의 파이프라인 하나의 프로세스, 하나의 파이프라인 많은 프로세스, 많은 독립 파이프라인
데이터 소유권 공유 데이터베이스; 모든 코드가 모든 테이블에 접근 가능 공유 데이터베이스; 모듈들이 관례에 의해 자체 스키마 영역을 소유 각 서비스가 자체 데이터베이스를 소유; 공유 테이블 없음
실패 모드 하나의 프로세스 충돌이 전체 앱을 다운시킴 하나의 프로세스 충돌이 전체 앱을 다운시킴 서비스 실패가 격리됨; 다른 것들은 우아하게 저하됨
팀 적합성 1~3개 팀에 최적; 팀 수가 늘면 마찰 증가 2~8개 팀에 최적; 모듈이 팀 소유권에 맞춰짐 독립적인 주기로 출시하는 5개 이상 팀에 필요
운영 비용 낮음: 하나의 배포, 하나의 로그 스트림, 하나의 알림 세트 낮음: 일반 모놀리스와 동일한 운영 발자국 높음: 관측성, 컨테이너 오케스트레이션, 서비스 메쉬, 서비스별 CI/CD
트랜잭션 모델 사소하게 전체 ACID 트랜잭션 사소하게 전체 ACID 트랜잭션 사가 또는 결과적 일관성; 분산 트랜잭션은 어려움
사용 시기 새 제품, 소규모 팀, 알려지지 않은 도메인 명확한 도메인 경계를 가진 성장하는 제품, 10~100명 엔지니어 여러 자율 팀, 검증된 도메인 모델, 실제 독립적 확장 요구

회사 규모별 솔직한 시각

아키텍처 조언은 마이크로서비스가 의미 있는 임계값을 이미 넘은 회사들에서 오는 경향이 있습니다 — 왜냐하면 그런 회사들이 엔지니어링 블로그, 컨퍼런스 발표, 자세한 포스트모템을 작성할 자원을 가진 곳이기 때문입니다. 이것이 생존 편향을 만듭니다. 더 솔직한 지도입니다:

세 단계 진화: 모놀리스, Modular Monolith, 마이크로서비스가 왼쪽에서 오른쪽으로 화살표와 함께 표시됨. STAGE 1 STAGE 2 STAGE 3 Monolith One deployable All Code one process shared DB enforce boundaries Modular Monolith One deployable · internal modules Billing Orders Notif. shared DB · owned schemas split at seams Billing Orders Notif. own DBs · network calls
세 가지 아키텍처 단계. 1단계: 하나의 박스, 모든 것이 함께. 2단계: 강제된 내부 모듈 경계(점선)가 있는 하나의 박스. 3단계: 네트워크 호출(점선 화살표)로 연결된 별도의 박스들, 각각 자체 데이터베이스 실린더를 가짐. 어느 단계에서든 멈출 수 있습니다 — 올바른 모양은 유행이 아닌 팀에 달려 있습니다.

스타트업 (1~15명 엔지니어). 모놀리스를 구축하세요. 시스템의 어느 부분이 확장되어야 할지, 어떤 기능이 살아남을지, 또는 어떤 도메인 경계가 실제인지 아직 모릅니다. 조기 분해는 결정하기에 충분한 정보가 있기 전에 결정을 고착시킵니다. Shopify, GitHub, Basecamp는 모두 Rails 모놀리스로 시작했습니다. Twitter도 마찬가지였으며, 상당한 규모에서 수년간 실행했다가 진짜 부하에서 분해했습니다.

스케일업 (15~80명 엔지니어, 성장 중). 이곳이 Modular Monolith가 가치를 발휘하는 곳입니다. 단일 코드베이스의 통제되지 않은 성장이 실제 마찰을 일으킬 만큼 충분한 엔지니어가 있지만, 여전히 팀으로 배포하고 전체 마이크로서비스의 오버헤드가 속도를 죽일 것입니다. 모듈 경계, 팀 소유권, 깔끔한 인터페이스에 투자하세요. 서비스를 추출하는 옵션을 유지하되, 구체적인 확장 요구가 강제하지 않는 한 아직 행사하지 마세요.

엔터프라이즈 / 대규모 조직 (80명+ 엔지니어, 여러 자율 팀). 선택적 마이크로서비스가 여기서 의미가 있습니다 — 하지만 선택적이 핵심 단어입니다. 가장 효과적인 대규모 아키텍처들은 순수 모놀리스도, 동일한 크기의 서비스의 바다도 아닙니다: 그들은 사려 깊은 혼합입니다. 가장 부하가 높고 도메인 중심적인 기능을 위한 몇 가지 핵심 서비스, 운영 중간을 위한 Modular Monolith, 그리고 독립적 확장이나 컴플라이언스가 진정으로 요구하는 몇 가지 특화된 서비스. Amazon은 모든 것을 한 번에 분해하지 않았습니다; 그들은 부하에서 이음새를 식별하고 하나씩 추출했습니다.

Netflix와 Amazon의 진짜 교훈

Netflix와 Amazon이 자신들의 마이크로서비스 여정을 설명할 때, 인용되는 부분은 수백 개의 서비스가 있는 아키텍처 다이어그램입니다. 빠지는 부분은 그들이 모놀리스로 시작해서, 고통이 부정할 수 없을 때까지 실행하다가, 전환에 수년과 엄청난 엔지니어링 노력을 투자했다는 것입니다 — 이제 마이크로서비스를 실행 가능하게 만드는 많은 도구를 구축하는 것을 포함하여. 그들은 어디에 도달했는지를 말해주는 것이지, 어디서 시작해야 하는지가 아닙니다.

핵심 요약

  • 모놀리스가 올바른 기본값입니다. 구축하기 단순하고, 디버깅하기 쉬우며, 사소하게 트랜잭션적입니다. 고통은 과대광고가 시사하는 것보다 늦게 오며 진짜 규모에서만 옵니다.
  • Modular Monolith는 가장 활용도가 낮은 옵션입니다. 내부 모듈 간의 강제된 경계는 팀 명확성, 안전한 리팩터링, 추출 준비 이음새를 제공합니다 — 어떤 분산 시스템 복잡성도 없이.
  • 마이크로서비스는 기능이 아닌 세금입니다. 네트워크 실패, 결과적 일관성, 분산 추적, 운영 오버헤드로 지불합니다. 보상 — 독립적 배포, 독립적 확장, 팀 자율성 — 은 실제이지만 그것을 요구하는 규모에서만.
  • 분산 모놀리스가 최악의 결과입니다. 함께 배포해야 하는 단단히 결합된 서비스들은 마이크로서비스의 모든 비용과 혜택은 하나도 제공합니다. 기술 레이어가 아닌 경계 컨텍스트를 따라 분리하세요.
  • 데이터 소유권이 어려운 제약입니다. 다른 서비스와 데이터베이스를 공유하는 서비스는 서비스가 아닙니다. 각 서비스는 자체 데이터를 소유해야 합니다; 경계를 넘는 일관성은 명시적인 설계가 필요합니다.
  • 점진적으로 분리하기 위해 교살자 무화과 패턴을 사용하세요. 한 번에 하나의 경계 컨텍스트를 추출하세요. 각 추출이 무언가를 가르쳐 주며 되돌릴 수 있습니다. 빅뱅 분해는 절대 하지 마세요.
  • 아키텍처를 팀 규모에 맞추세요, 열망이 아닌. 스타트업: 모놀리스. 스케일업: Modular Monolith. 대규모 자율 팀: 진정으로 필요한 곳에 선택적 마이크로서비스.

아키텍처 결정은 시간이 지남에 따라 파급되고 복리로 증가합니다. 분리하기로 결정한 후 많은 팀들이 마주하는 다음 질문은 프론트엔드에 무엇을 할 것인가입니다 — 단일 통합 UI를 제공할지 그것도 분해할지. 그 토끼굴은 마이크로 프론트엔드: 언제, 왜, 그리고 실제로 무엇이 드는가에서 탐구합니다.