버전이 지정된 API의 기본 코드베이스를 어떻게 관리합니까?


104

ReST API에 대한 버전 관리 전략에 대해 읽어 봤는데 그중 어느 것도 기본 코드베이스를 관리하는 방법이 해결되지 않는 것 같습니다.

예를 들어, 단일 필드 대신 개별 forenamesurname필드를 반환하도록 고객 리소스를 변경하는 등 API에 많은 주요 변경 사항을 적용한다고 가정 해 보겠습니다 name. (이 예에서는 관련된 개념을 이해하기 쉽기 때문에 URL 버전 관리 솔루션을 사용하지만 질문은 콘텐츠 협상 또는 사용자 지정 HTTP 헤더에도 동일하게 적용됩니다.)

이제에 엔드 포인트가 http://api.mycompany.com/v1/customers/{id}있고에 호환되지 않는 다른 엔드 포인트가 http://api.mycompany.com/v2/customers/{id}있습니다. 우리는 여전히 v1 API에 대한 버그 수정 및 보안 업데이트를 릴리스하고 있지만 새로운 기능 개발은 이제 모두 v2에 초점을 맞추고 있습니다. API 서버에 변경 사항을 작성, 테스트 및 배포하는 방법은 무엇입니까? 적어도 두 가지 해결책을 볼 수 있습니다.

  • v1 코드베이스에 소스 제어 분기 / 태그를 사용합니다. v1 및 v2는 두 버전 모두에 동일한 버그 수정을 적용하는 데 필요한 개정 제어 병합을 사용하여 독립적으로 개발 및 배포됩니다. 이전 버전을 계속 지원하면서 주요 새 버전을 개발할 때 네이티브 앱의 코드베이스를 관리하는 방법과 유사합니다.

  • 코드베이스 자체가 API 버전을 인식하도록하여 v1 고객 표현과 v2 고객 표현을 모두 포함하는 단일 코드베이스로 끝납니다. 배포 문제 대신 솔루션 아키텍처의 일부로 버전 관리를 처리하세요. 아마도 네임 스페이스와 라우팅의 일부 조합을 사용하여 요청이 올바른 버전에서 처리되는지 확인하세요.

브랜치 모델의 분명한 장점은 이전 API 버전을 삭제하는 것이 간단하다는 것입니다. 적절한 브랜치 / 태그 배포를 중지하면됩니다.하지만 여러 버전을 실행하는 경우 실제로 복잡한 브랜치 구조와 배포 파이프 라인이 될 수 있습니다. "통합 코드베이스"모델은이 문제를 피하지만 더 이상 필요하지 않은 코드베이스에서 더 이상 사용되지 않는 리소스와 엔드 포인트를 제거하기가 훨씬 더 어려워집니다. 간단한 정답이있을 것 같지 않기 때문에 이것이 아마도 주관적이라는 것을 알고 있지만 여러 버전에서 복잡한 API를 유지 관리하는 조직이이 문제를 해결하는 방법을 이해하고 싶습니다.


41
이 질문을 해주셔서 감사합니다! 나는 더 많은 사람들이이 질문에 대답하지 않는다는 것을 믿을 수 없다 !! 나는 모든 사람들이 버전이 시스템에 어떻게 들어가는 지에 대해 의견을 갖는 것에 지치고 지 쳤지 만, 적절한 코드로 버전을 발송하는 진짜 어려운 문제를 해결하는 사람은 아무도없는 것 같습니다. 지금 쯤이면이 일반적인 문제에 대해 받아 들여진 "패턴"또는 "솔루션"의 배열이 있어야합니다. "API 버전 관리"에 관한 질문이 엄청나게 많습니다. 버전을 수락하는 방법을 결정하는 것은 FRIKKIN 간단합니다 (상대적으로)! 일단 들어가면 코드베이스에서 처리하는 것은 어렵습니다!
arijeet

답변:


45

말씀하신 두 가지 전략을 모두 사용했습니다. 이 두 가지 중에서 두 번째 접근 방식을 선호합니다.이를 지원하는 사용 사례에서 더 간단합니다. 즉, 버전 관리 요구 사항이 간단하다면 더 간단한 소프트웨어 설계를 사용하십시오.

  • 적은 수의 변경, 낮은 복잡성 변경 또는 낮은 빈도의 변경 일정
  • 나머지 코드베이스와 거의 직교하는 변경 : 공용 API는 코드에서 분기를 "과도하게"(선택한 용어의 정의에 관계없이) 요구하지 않고 나머지 스택과 평화롭게 존재할 수 있습니다.

이 모델을 사용하여 더 이상 사용되지 않는 버전을 제거하는 것이 지나치게 어렵지 않았습니다.

  • 좋은 테스트 커버리지는 폐기 된 API 및 관련 지원 코드를 제거하여 회귀가 없음을 보장 함을 의미합니다.
  • 좋은 이름 지정 전략 (API 버전 패키지 이름 또는 메서드 이름의 다소 추악한 API 버전)을 통해 관련 코드를 쉽게 찾을 수 있습니다.
  • 교차 우려는 더 어렵습니다. 여러 API를 지원하기위한 핵심 백엔드 시스템의 수정은 매우 신중하게 평가되어야합니다. 어느 시점에서 백엔드 버전 관리 비용 (위의 "과도한"에 대한 설명 참조)이 단일 코드베이스의 이점을 능가합니다.

첫 번째 접근 방식은 공존하는 버전 간의 충돌을 줄이는 관점에서 확실히 더 간단하지만 별도의 시스템을 유지 관리하는 오버 헤드가 버전 충돌을 줄이는 이점을 능가하는 경향이 있습니다. 즉, 새로운 공용 API 스택을 세우고 별도의 API 분기에서 반복을 시작하는 것은 매우 간단했습니다. 물론 세대 별 손실은 거의 즉시 시작되었고 분기는 병합, 병합 충돌 해결 및 기타 재미로 변했습니다.

세 번째 접근 방식은 아키텍처 계층에 있습니다. Facade 패턴의 변형을 채택하고 API를 적절한 Facade 인스턴스와 통신하는 공용 버전 계층으로 추상화 한 다음 자체 API 세트를 통해 백엔드와 통신합니다. 귀하의 Facade (이전 프로젝트에서 어댑터를 사용했습니다)는 자체 포함되고 테스트 가능한 자체 패키지가되어 백엔드 및 서로 독립적으로 프런트 엔드 API를 마이그레이션 할 수 있습니다.

이는 API 버전이 동일한 종류의 리소스를 노출하는 경향이 있지만 전체 이름 / 외명 / 성 예에서와 같이 구조적 표현이 다른 경우에 작동합니다. "내 백엔드 서비스가 공개 API v1에 노출 된 잘못 계산 된 복리를 반환했습니다. 고객이 이미이 잘못된 동작을 패치했습니다. 따라서 업데이트 할 수 없습니다."와 같이 다른 백엔드 계산에 의존하기 시작하면 약간 더 어려워집니다. 백엔드에서 계산하고 v2까지 적용합니다. 따라서 이제이자 계산 코드를 포크해야합니다. " 운 좋게도 이러한 경우는 드물게 발생하는 경향이 있습니다. 실제로 RESTful API의 소비자는 이론적으로 멱 등성 GET테드 리소스 에 대한 변경 사항이없는 경우에도 버그 간 하위 호환성보다 정확한 리소스 표현을 선호 합니다.

최종 결정을 듣고 싶습니다.


5
소스 코드에서 변경되지 않은 v0과 v1간에 모델을 복제합니까? 아니면 v1이 일부 v0 모델을 사용합니까? 나에게 v1이 일부 필드에 v0 모델을 사용하는 것을 본다면 혼란 스러울 것입니다. 그러나 다른 한편으로는 코드 팽창을 줄일 수 있습니다. 여러 버전을 처리하기 위해 우리는 변경되지 않은 모델에 대한 복제 코드를 받아들이고 살아야합니까?
EdgeCaseBerg

1
내 기억은 API 자체와 독립적으로 소스 코드 버전이 지정된 모델이므로 예를 들어 API v1은 Model V1을 사용할 수 있고 API v2는 Model V1도 사용할 수 있습니다. 기본적으로 공개 API에 대한 내부 종속성 그래프에는 노출 된 API 코드와 서버 및 모델 코드와 같은 백엔드 "이행"코드가 모두 포함되었습니다. 여러 버전의 경우 내가 사용한 유일한 전략은 전체 스택을 복제하는 것입니다. 하이브리드 접근 방식 (모듈 A는 복제, 모듈 B는 버전 관리 ...)이 매우 혼란스러워 보입니다. 물론 YMMV. :)
Palpatim

2
세 번째 접근 방식에 대해 제안 된 내용을 따르는 지 잘 모르겠습니다. 이와 같이 구조화 된 코드의 공개 예제가 있습니까?
Ehtesh Choudhury

13

저에게는 두 번째 접근 방식이 더 좋습니다. SOAP 웹 서비스에 사용했으며 REST에도 사용할 계획입니다.

작성할 때 코드베이스는 버전을 인식해야하지만 호환성 레이어를 별도의 레이어로 사용할 수 있습니다. 귀하의 예제에서 코드베이스는 이름과 성을 사용하여 리소스 표현 (JSON 또는 XML)을 생성 할 수 있지만 호환성 계층은 대신 이름 만 갖도록 변경합니다.

코드베이스는 최신 버전, 즉 v3 만 구현해야합니다. 호환성 계층은 최신 버전 v3과 지원되는 버전 (예 : v1 및 v2) 간의 요청 및 응답을 변환해야합니다. 호환성 계층에는 체인으로 연결할 수있는 지원되는 각 버전에 대해 별도의 어댑터가있을 수 있습니다.

예를 들면 :

클라이언트 v1 요청 : v1은 v2에 적응 ---> v2는 v3에 적응 ----> 코드베이스

클라이언트 v2 요청 : v1은 v2에 적응 (건너 뛰기) ---> v2는 v3에 적응 ----> 코드베이스

응답을 위해 어댑터는 단순히 반대 방향으로 작동합니다. Java EE를 사용하는 경우 예를 들어 서블릿 필터 체인을 어댑터 체인으로 사용할 수 있습니다.

하나의 버전을 제거하는 것은 쉽습니다. 해당 어댑터와 테스트 코드를 삭제하십시오.


전체 기본 코드베이스가 변경된 경우 호환성을 보장하기가 어렵습니다. 버그 수정 릴리스를 위해 이전 코드베이스를 유지하는 것이 훨씬 안전합니다.
Marcelo Cantos

5

분기는 나에게 훨씬 더 좋아 보이며 내 경우에는이 접근 방식을 사용했습니다.

예, 이미 언급했듯이 버그 수정을 백 포팅하려면 약간의 노력이 필요하지만 동시에 하나의 소스 기반 (라우팅 및 기타 모든 항목 포함)에서 여러 버전을 지원하려면 최소한 동일한 노력이 필요하여 시스템을 더 많이 만듭니다. 내부에 논리의 다른 분기가있는 복잡하고 괴물입니다 (버전 관리의 어느 시점에서 정의 할 때 case()코드가 중복되거나 더 나쁜 버전 모듈을 가리키게 될 것입니다 if(version == 2) then...). 또한 회귀 목적으로 테스트를 분기로 유지해야한다는 사실을 잊지 마십시오.

버전 관리 정책과 관련하여 : 현재 버전에서 최대 -2 개 버전을 유지하고 이전 버전에 대한 지원을 중단하므로 사용자가 이동하도록 동기를 부여합니다.


현재 단일 코드베이스에서 테스트하는 것에 대해 생각하고 있습니다. 테스트는 항상 분기되어야한다고 언급했지만 v1, v2, v3 등에 대한 모든 테스트도 동일한 솔루션에있을 수 있고 모두 동시에 실행될 수 있다고 생각합니다. 나는 그들이 지원하는 어떤 버전을 지정하는 속성으로 테스트를 장식 생각 해요 : 예 [Version(From="v1", To="v2")], [Version(From="v2", To="v3")], [Version(From="v1")] // All versions 그냥 지금 탐험, 이제까지 그것을 사람을 들었다?
Lee Gunn

1
글쎄, 3 년 후에 나는 원래의 질문에 대한 정확한 답이 없다는 것을 알게되었다. : D. 매우 프로젝트에 따라 다릅니다. API를 동결하고 유지 (예 : 버그 수정) 만 할 수 있다면 관련 코드 (API 관련 비즈니스 로직 + 테스트 + 나머지 엔드 포인트)를 분기 / 분리하고 모든 공유 항목을 별도의 라이브러리 (자체 테스트 포함)에 보관합니다. ). V1이 V2와 꽤 오랫동안 공존하고 기능 작업이 여전히 진행 중이라면 나는 그것들을 함께 유지하고 테스트 할 것입니다 (V1, V2 등을 포함하고 그에 따라 이름을 지정 함).
edmarisov

1
감사. 네, 꽤 독단적 인 공간 인 것 같습니다. 먼저 하나의 솔루션 접근 방식을 시도하고 어떻게 진행되는지 볼 것입니다.
Lee Gunn

0

일반적으로 여러 버전을 유지해야하는 상황으로 이끄는 주요 버전의 API를 도입하는 것은 자주 발생하지 않거나 발생하지 않아야하는 이벤트입니다. 그러나 완전히 피할 수는 없습니다. 일단 도입 된 메이저 버전이 비교적 오랜 기간 동안 최신 버전을 유지한다는 것이 전반적으로 안전한 가정이라고 생각합니다. 이를 바탕으로 나는 최신 버전에 변경 사항을 도입 할 때 이전 버전을 깨지 않을 것이라는 확신을 더 많이 제공하므로 중복을 희생하면서 코드를 단순하게 만드는 것을 선호합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.