Sürümü belirlenmiş bir API için temel kod tabanını nasıl yönetirsiniz?


105

ReST API'leri için sürüm oluşturma stratejilerini okudum ve hiçbirinin ele almadığı bir şey, temeldeki kod tabanını nasıl yönettiğinizdir.

Diyelim ki bir API'de bir dizi son derece değişiklik yapıyoruz - örneğin, Müşteri kaynağımızı tek bir alan yerine ayrı forenameve surnamealanları döndürecek şekilde değiştiriyoruz name. (Bu örnek için, ilgili kavramları anlamak kolay olduğu için URL sürüm oluşturma çözümünü kullanacağım, ancak soru içerik pazarlığı veya özel HTTP üstbilgileri için de aynı derecede geçerlidir)

Şimdi bir uç noktamız var http://api.mycompany.com/v1/customers/{id}ve uyumsuz başka bir uç noktamız var http://api.mycompany.com/v2/customers/{id}. Hala v1 API için hata düzeltmeleri ve güvenlik güncellemeleri yayınlıyoruz, ancak yeni özellik geliştirmenin tamamı artık v2'ye odaklanıyor. API sunucumuzdaki değişiklikleri nasıl yazar, test eder ve dağıtırız? En az iki çözüm görebiliyorum:

  • V1 kod tabanı için bir kaynak kontrol dalı / etiketi kullanın. v1 ve v2, önceki sürümü desteklerken büyük bir yeni sürüm geliştirirken yerel uygulamalar için kod tabanlarını yönetme şekline benzer şekilde, her iki sürüme de aynı hata düzeltmesini uygulamak için gerektiği şekilde kullanılan revizyon denetimi birleştirmeleriyle bağımsız olarak geliştirilir ve dağıtılır.

  • Kod tabanının API sürümlerinden haberdar olmasını sağlayın, böylece hem v1 müşteri temsilini hem de v2 müşteri temsilini içeren tek bir kod tabanı elde edersiniz. Sürüm oluşturmayı bir dağıtım sorunu yerine çözüm mimarinizin bir parçası olarak ele alın - muhtemelen isteklerin doğru sürüm tarafından işlendiğinden emin olmak için bazı ad alanları ve yönlendirme kombinasyonlarını kullanın.

Şube modelinin bariz avantajı, eski API sürümlerini silmenin önemsiz olmasıdır - sadece uygun dalı / etiketi dağıtmayı bırakın - ancak birkaç sürüm çalıştırıyorsanız, gerçekten kıvrımlı bir dal yapısı ve dağıtım hattına sahip olabilirsiniz. "Birleşik kod tabanı" modeli bu sorunu ortadan kaldırır, ancak (sanırım?) Artık gerekli olmadıklarında kullanımdan kaldırılmış kaynakları ve uç noktaları kod tabanından kaldırmayı çok daha zor hale getirecektir. Bunun muhtemelen öznel olduğunu biliyorum, çünkü basit bir doğru cevap olma ihtimali düşük, ancak birden çok sürümde karmaşık API'leri kullanan kuruluşların bu sorunu nasıl çözdüğünü anlamayı merak ediyorum.


41
Bu soruyu sorduğun için teşekkürler! Daha fazla insanın bu soruyu cevaplamadığına İNANAMIYORUM !! Sürümlerin bir sisteme nasıl girdiği konusunda herkesin fikir sahibi olmasından bıktım usandım, ancak hiç kimse sürümleri uygun kodlarına göndermenin gerçek zor sorununu çözemiyor gibi görünüyor. Şimdiye kadar, görünüşte yaygın olan bu soruna en az bir dizi kabul edilmiş "kalıp" veya "çözüm" olmalıdır. SO hakkında "API versiyonlama" ile ilgili çok sayıda soru var. Sürümlerin nasıl kabul edileceğine karar vermek FRIKKIN BASİTtir (nispeten)! İçeri girdikten sonra kod tabanında ele almak ZOR!
arijeet

Yanıtlar:


45

Bahsettiğiniz her iki stratejiyi de kullandım. Bu ikisinden, onu destekleyen kullanım durumlarında daha basit olan ikinci yaklaşımı tercih ediyorum. Yani, sürüm oluşturma ihtiyaçları basitse, daha basit bir yazılım tasarımıyla devam edin:

  • Düşük sayıda değişiklik, düşük karmaşıklık değişiklikleri veya düşük frekanslı değişiklik programı
  • Kod tabanının geri kalanına büyük ölçüde ortogonal olan değişiklikler: Genel API, kodda dallanma "aşırı" (o terimin hangi tanımı için seçerseniz seçin) gerektirmeden yığının geri kalanıyla barış içinde var olabilir

Bu modeli kullanarak kullanımdan kaldırılmış sürümleri kaldırmayı çok zor bulmadım:

  • İyi bir test kapsamı, kullanımdan kaldırılmış bir API'nin ve ilgili destek kodunun sökülmesinin hiçbir (iyi, minimum) gerileme sağlamadığı anlamına geliyordu
  • İyi adlandırma stratejisi (API sürümlü paket adları veya yöntem adlarında biraz daha çirkin, API sürümleri) ilgili kodun bulunmasını kolaylaştırdı
  • Kesişen endişeler daha zordur; Birden çok API'yi desteklemek için temel arka uç sistemlerinde yapılan değişikliklerin çok dikkatli bir şekilde tartılması gerekir. Bir noktada, arka uçta sürüm oluşturma maliyeti (yukarıdaki "aşırı" konusundaki yoruma bakın), tek bir kod tabanının avantajından daha ağır basar.

İlk yaklaşım, birlikte var olan sürümler arasındaki çatışmayı azaltma açısından kesinlikle daha basittir, ancak ayrı sistemleri sürdürmenin ek yükü, sürüm çatışmasını azaltmanın faydasından daha ağır basma eğilimindeydi. Bununla birlikte, yeni bir genel API yığınını ayağa kaldırmak ve ayrı bir API şubesinde yinelemeye başlamak son derece basitti. Tabii ki, kuşaksal kayıp neredeyse anında ortaya çıktı ve dallar birleşme, çatışma çözümleri ve benzeri diğer eğlenceler karmaşasına dönüştü.

Üçüncü bir yaklaşım, mimari katmandadır: Facade modelinin bir varyantını benimseyin ve API'lerinizi, uygun Facade örneğiyle konuşan herkese açık, versiyonlanmış katmanlara soyutlayın, bu da kendi API kümesi aracılığıyla arka uçla iletişim kurar. Cepheniz (önceki projemde bir Adaptör kullandım) kendi paketi haline gelir, bağımsız ve test edilebilir hale gelir ve ön uç API'lerini arka uçtan ve birbirinden bağımsız olarak taşımanıza olanak tanır.

Bu, API sürümleriniz aynı tür kaynakları açığa çıkarma eğilimindeyse, ancak tam adınız / adınız / soyadınız örneğinde olduğu gibi farklı yapısal temsillerle çalışacaktır. Farklı arka uç hesaplamalarına güvenmeye başlarlarsa biraz daha zorlaşır, "Arka uç hizmetim, genel API v1'de açığa çıkan yanlış hesaplanmış bileşik faiz döndürdü. Müşterilerimiz bu hatalı davranışı zaten yamalamışlardır. Bu nedenle, bunu güncelleyemiyorum. arka uçta hesaplama yapın ve v2'ye kadar geçerli olmasını sağlayın. Bu nedenle, şimdi faiz hesaplama kodumuzu çatallamamız gerekiyor. " Neyse ki, bunlar seyrek olma eğilimindedir: pratik olarak konuşursak, RESTful API'lerin tüketicileri, teorik olarak idempotentleştirilmiş bir GETkaynak üzerindeki kesintisiz değişiklikler arasında bile, hataya karşı hataya karşı geriye dönük uyumluluk yerine doğru kaynak temsillerini tercih ederler .

Nihai kararınızı duymakla ilgileneceğim.


5
Merak ediyorum, kaynak kodda, v0 ve v1 arasında değişmeyen modelleri çoğaltıyor musunuz? Veya v1'in bazı v0 modellerini kullanıyor musunuz? V1'in bazı alanlar için v0 modellerini kullandığını görürsem kafam karışır. Ancak öte yandan, kod şişkinliğini azaltacaktır. Birden fazla sürümü işlemek için, hiç değişmeyen modeller için yinelenen kodu kabul etmemiz ve bunlarla yaşamamız mı gerekiyor?
EdgeCaseBerg

1
Hatırladığım kadarıyla, kaynak kodumuz API'nin kendisinden bağımsız olarak modellere uyarlanmıştır, bu nedenle örneğin API v1 Model V1'i kullanabilir ve API v2 de Model V1'i kullanabilir. Temel olarak, genel API için dahili bağımlılık grafiği, hem açık API kodunu hem de sunucu ve model kodu gibi arka uç "yerine getirme" kodunu içeriyordu. Birden çok sürüm için, şimdiye kadar kullandığım tek strateji tüm yığının kopyalanmasıdır - karma bir yaklaşım (modül A kopyalanır, modül B sürümlendirilir ...) çok kafa karıştırıcı görünüyor. Elbette YMMV. :)
Palpatim

2
Üçüncü yaklaşım için önerilenleri takip ettiğimden emin değilim. Bunun gibi yapılandırılmış herkese açık kod örnekleri var mı?
Ehtesh Choudhury

13

Benim için ikinci yaklaşım daha iyi. SOAP web servisleri için kullandım ve REST için de kullanmayı planlıyorum.

Siz yazarken, kod tabanı sürümden haberdar olmalıdır, ancak bir uyumluluk katmanı ayrı bir katman olarak kullanılabilir. Örneğinizde, kod tabanı ad ve soyad ile kaynak gösterimi (JSON veya XML) üretebilir, ancak uyumluluk katmanı onu bunun yerine yalnızca ada sahip olacak şekilde değiştirecektir.

Kod tabanı yalnızca en son sürümü uygulamalıdır, diyelim ki v3. Uyumluluk katmanı, en yeni sürüm v3 ile desteklenen sürümler (ör. V1 ve v2) arasındaki istekleri ve yanıtı dönüştürmelidir. Uyumluluk katmanı, zincir olarak bağlanabilen desteklenen her sürüm için ayrı bir adaptöre sahip olabilir.

Örneğin:

İstemci v1 isteği: v1, v2'ye uyar -> v2, v3'e uyar ----> kod tabanı

İstemci v2 isteği: v1, v2'ye uyar (atla) -> v2, v3'e uyar ----> kod tabanı

Yanıt için adaptörler basitçe ters yönde çalışır. Java EE kullanıyorsanız, örneğin adaptör zinciri olarak sunucu uygulaması filtre zincirini kullanabilirsiniz.

Bir sürümü kaldırmak kolaydır, ilgili adaptörü ve test kodunu silin.


Temel kod tabanının tamamı değişmişse uyumluluğu garanti etmek zordur. Hata düzeltme sürümleri için eski kod tabanını korumak çok daha güvenlidir.
Marcelo Cantos

5

Dallanma benim için çok daha iyi görünüyor ve bu yaklaşımı benim durumumda kullandım.

Evet, daha önce de belirttiğiniz gibi - backport hata düzeltmeleri biraz çaba gerektirecek, ancak aynı zamanda tek bir kaynak tabanında (yönlendirme ve diğer tüm şeyler ile) birden çok sürümü desteklemek, daha az olmasa da, en azından aynı çabayı gerektirecek ve sistemi daha fazla hale getirecektir. İçinde farklı mantık dallarıyla karmaşık ve canavarca (sürümlemenin bir noktasında kesinlikle case()kodun çoğaltıldığı veya daha da kötüsü olan sürüm modüllerine işaret edeceksiniz if(version == 2) then...). Ayrıca, regresyon amacıyla testleri dallara ayrılmış halde tutmanız gerektiğini de unutmayın.

Sürüm oluşturma politikası ile ilgili olarak: Mevcut sürümlerden en fazla -2 sürüm tutacaktım, eski sürümler için desteği kullanımdan kaldıracaktım - bu, kullanıcıların taşınması için biraz motivasyon sağlayacaktır.


Şu anda tek bir kod tabanında test etmeyi düşünüyorum. Testlerin her zaman dallanmış olması gerektiğini söylediniz, ancak v1, v2, v3 vb. İçin tüm testlerin de aynı çözümde yaşayabileceğini ve hepsinin aynı anda çalıştırılabileceğini düşünüyorum. Ben destekledikleri Ne versiyonları belirtmek hangi özelliklere sahip testler dekorasyon düşünüyorum: örneğin [Version(From="v1", To="v2")], [Version(From="v2", To="v3")], [Version(From="v1")] // All versions Hemen şimdi keşfetmek, Hiç kimseyi do duydu?
Lee Gunn

1
Eh, 3 yıl sonra, orijinal soruya kesin bir cevap olmadığını öğrendim: D. Çok projeye bağlıdır. API'yi dondurmayı göze alabiliyorsanız ve yalnızca bakımını yapabiliyorsanız (örneğin, hata düzeltmeleri), ilgili kodu (API ile ilgili iş mantığı + testler + dinlenme uç noktası) yine de dallandırırım / ayırırım ve tüm paylaşılan öğeleri ayrı kitaplıkta (kendi testleriyle) ). V1, uzun bir süre V2 ile birlikte var olacaksa ve özellik çalışmaları devam ediyorsa, onları bir arada tutacağım ve testleri de yapacağım (V1, V2 vb. Ve buna göre adlandırılmış).
edmarisov

1
Teşekkürler. Evet, oldukça kararlı bir alan gibi görünüyor. Önce tek çözüm yaklaşımını deneyeceğim ve nasıl gittiğini göreceğim.
Lee Gunn

0

Genellikle, birden fazla sürümü sürdürmek zorunda kalmanıza neden olan büyük bir API sürümünün tanıtımı, çok sık gerçekleşmeyen (veya olmaması gereken) bir olaydır. Ancak tamamen önlenemez. Genel olarak, bir ana sürümün piyasaya sürüldüğünde, nispeten uzun bir süre için en son sürüm olarak kalacağının güvenli bir varsayım olduğunu düşünüyorum. Buna dayanarak, en son sürümde değişiklikler yaptığımda önceki sürümü bozmama konusunda bana daha fazla güven verdiğinden, çoğaltma pahasına kodda basitlik elde etmeyi tercih ederim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.