Mikro hizmetler için API 'Leri tasarlama
İyi API tasarımı, mikro hizmetler mimarisinde önemli olduğundan, hizmetler arasındaki tüm veri alışverişi iletiler veya API çağrıları aracılığıyla gerçekleştiğinden. Geveze g/çoluşturmaktan kaçınmak Için API 'lerin etkili olması gerekir. Hizmetler bağımsız olarak çalışan takımlar tarafından tasarlandığından, güncelleştirmelerin diğer hizmetleri bozmaması için API 'Lerin iyi tanımlanmış semantik ve sürüm oluşturma düzenlerine sahip olması gerekir.

İki tür API arasında ayrım yapmak önemlidir:
- İstemci uygulamalarının çağrı kullandığı ortak API 'Ler.
- Interservice iletişimi için kullanılan arka uç API 'Leri.
Bu iki kullanım durumu biraz farklı gereksinimlere sahiptir. Ortak bir API, istemci uygulamalarıyla, genellikle tarayıcı uygulamalarıyla veya yerel mobil uygulamalarla uyumlu olmalıdır. Çoğu zaman, genel API 'nin geri kalanı HTTP üzerinden kullanacağı anlamına gelir. Ancak arka uç API 'Leri için, ağ performansını hesaba getirmeniz gerekir. Hizmetlerinizin ayrıntı düzeyine bağlı olarak, Interservice iletişimi çok fazla ağ trafiğine neden olabilir. Hizmetler hızla g/ç 'ye bağlanabilir. Bu nedenle, serileştirme hızı ve yük boyutu gibi hususlar daha önemli hale gelir. HTTP üzerinden REST kullanmanın bazı popüler alternatifleri gRPC, Apache avro ve Apache Thrift ' i içerir. Bu protokoller ikili serileştirme destekler ve genellikle HTTP 'den daha etkilidir.
Dikkat edilmesi gerekenler
Bir API 'nin nasıl uygulanacağını seçerken göz önünde bulundurmanız gereken bazı şeyler aşağıda verilmiştir.
Rest Ile RPC karşılaştırması. Bir REST stili arabirim kullanma ve RPC stili arabirim arasındaki avantajları göz önünde bulundurun.
REST modeller kaynakları, etki alanı modelinizi ifade etmenin doğal bir yolu olabilir. Bu, gelişibilirliği teşvik eden HTTP fiillerini temel alan bir Tekdüzen arabirimini tanımlar. Bu, ıdesotlik, yan etkiler ve yanıt kodları bakımından iyi tanımlanmış semantiğe sahiptir. Ve durum bilgisi olmayan iletişimi zorlar ve bu da ölçeklenebilirliği artırır.
RPC, işlemler veya komutlar etrafında daha fazla yönlendirilmelidir. RPC arabirimleri yerel Yöntem çağrıları gibi görüntiğinden, aşırı geveze API 'Leri tasarlamanıza yol açabilir. Ancak bu, RPC 'nin geveze olması anlamına gelmez. Yalnızca arabirimi tasarlarken dikkatli olmanız gerektiği anlamına gelir.
Yeniden bir arabirim için, en yaygın seçim JSON kullanılarak HTTP üzerinden GERI gönderilir. Bir RPC stili arabirim için, gRPC, Apache avro ve Apache Thrift gibi çeşitli popüler çerçeveler vardır.
Verimlilik. Hız, bellek ve yük boyutu açısından verimliliği göz önünde bulundurun. Genellikle bir gRPC tabanlı arabirim, HTTP üzerinden REST 'ten daha hızlıdır.
Arabirim tanım dili (IDL). Bir IDL, API 'nin yöntemlerini, parametrelerini ve dönüş değerlerini tanımlamak için kullanılır. Bir IDL, istemci kodu, serileştirme kodu ve API belgeleri oluşturmak için kullanılabilir. Idls, Postman gibi API test araçları tarafından da tüketilebilir. GRPC, avro ve Thrift gibi çerçeveler kendi IDL belirtimlerini tanımlar. HTTP üzerinde REST, standart bir IDL biçimine sahip değildir, ancak ortak bir seçim Openapı 'dir (eski adıyla Swagger). Ayrıca, resmi tanım dili kullanmadan bir HTTP REST API oluşturabilir, ancak kod oluşturma ve test avantajlarından yararlanabilirsiniz.
Serileştirme. Nesneler, tel üzerinden nasıl serileştirilir? Seçenekler metin tabanlı biçimleri (birincil olarak JSON) ve protokol arabelleği gibi ikili biçimleri içerir. İkili biçimler genellikle metin tabanlı biçimlerden daha hızlıdır. Ancak, çoğu dil ve çerçeve JSON serileştirmesini desteklediği için JSON 'ın birlikte çalışabilirlik açısından avantajları vardır. Bazı serileştirme biçimleri sabit bir şema gerektirir ve bazıları bir şema tanımı dosyası derlemeyi gerektirir. Bu durumda, bu adımı yapı sürecinizle eklemeniz gerekir.
Çerçeve ve dil desteği. HTTP, neredeyse her çerçevede ve dilde desteklenir. gRPC, avro ve Thrift hepsi C++, C#, Java ve Python için kitaplıklara sahiptir. Thrift ve gRPC de go 'Yu destekler.
Uyumluluk ve birlikte çalışabilirlik. GRPC gibi bir protokol seçerseniz, ortak API ile arka uç arasında bir protokol çevirisi katmanına sahip olabilirsiniz. Bir ağ geçidi , bu işlevi gerçekleştirebilir. Hizmet ağı kullanıyorsanız, hangi protokollerin hizmet ağıyla uyumlu olduğunu göz önünde bulundurun. Örneğin, linkerd, HTTP, Thrift ve gRPC için yerleşik desteğe sahiptir.
Bir ikili protokolün performans avantajlarına ihtiyaç duymadığınız sürece, temel önerimiz HTTP üzerinden REST ' i seçmiz. HTTP üzerinden REST için özel kitaplıklar gerekmez. Çağıranlar hizmet ile iletişim kurmak için bir istemci saplaması gerekli olmadığından, en az bir bağ oluşturur. Şema tanımlarını destekleyen araçların zengin ekosistemleri vardır, bunları test etmek ve yeniden takip edilen HTTP uç noktalarını izlemek. Son olarak, HTTP tarayıcı istemcileriyle uyumludur, bu nedenle istemci ile arka uç arasında bir protokol çevirisi katmanına gerek kalmaz.
Ancak, HTTP üzerinden REST ' i seçerseniz, senaryolarınız için yeterince iyi performans yapıp gerçekleştirmediğini doğrulamak üzere geliştirme sürecinde performansı ve yük testini daha erken yapmanız gerekir.
Yenilenmiş API tasarımı
Yeniden kullanılabilir API 'Ler tasarlamak için birçok kaynak vardır. Yararlı bulabileceğiniz bazı bilgiler şunlardır:
Göz önünde bulundurmanız gereken bazı özel noktalar aşağıda verilmiştir.
İç uygulama ayrıntılarını sızdıran veya yalnızca iç veritabanı şemasını yansıtan API 'Ler için izleyin. API, etki alanını modellemelidir. Bunlar, hizmetler arasında bir anlaşmada ve ideal olarak yalnızca yeni işlevsellik yeniden düzenlenmiş, ancak bir veritabanı tablosu normalleştirildiğinden yalnızca yeni işlev eklendiğinde değişir.
Mobil uygulama ve Masaüstü Web tarayıcısı gibi farklı türde istemci, farklı yük boyutları veya etkileşim desenleri gerektirebilir. Bu istemci için en iyi arabirimi kullanıma sunan her bir istemci için arka uçların arka uçlarını oluşturmak üzere ön uçlar için arka uç modelini kullanmayı düşünün.
Yan etkileri olan işlemler için, ıdempotent yapmayı ve bunları PUT yöntemleri olarak uygulamayı düşünün. Böylece, güvenli yeniden denemeler etkinleştirilir ve dayanıklılık iyileştirebilirler. Interservice iletişimi iletişim bu sorunu daha ayrıntılı bir şekilde ele almaktadır.
HTTP yöntemlerinde, yöntemin hemen yanıt döndürdüğü zaman uyumsuz semantiği olabilir, ancak hizmet işlemi zaman uyumsuz olarak yürütür. Bu durumda yöntem, isteğin işlenmek üzere kabul edildiğini belirten bir HTTP 202 yanıt kodu döndürmelidir, ancak işlem henüz tamamlanmadı. Daha fazla bilgi için bkz. zaman uyumsuz Request-Reply model.
KALANı DDD desenlerine eşleme
Varlık, toplama ve değer nesnesi gibi desenler, bazı kısıtlamaları etki alanı modelinizdeki nesnelere yerleştirmek için tasarlanmıştır. DDD 'ın pek çok tartışmasında desenler, oluşturucular veya özellik alıcıları ve ayarlayıcılar gibi nesne yönelimli (OO) dil kavramları kullanılarak modellenir. Örneğin, değer nesnelerinin sabit olması gerekir. Bir OO programlama dilinde, oluşturucudaki değerleri atayarak ve özellikleri salt okunurdur yaparak bunu zorunlu kılabilirsiniz:
export class Location {
readonly latitude: number;
readonly longitude: number;
constructor(latitude: number, longitude: number) {
if (latitude < -90 || latitude > 90) {
throw new RangeError('latitude must be between -90 and 90');
}
if (longitude < -180 || longitude > 180) {
throw new RangeError('longitude must be between -180 and 180');
}
this.latitude = latitude;
this.longitude = longitude;
}
}
Bu tür kodlama uygulamaları, geleneksel bir tek parçalı uygulama oluştururken özellikle önemlidir. Büyük bir kod tabanı sayesinde, birçok alt sistemi nesnesini kullanabilir Location , bu nedenle nesnenin doğru davranışı zorlaması önemlidir.
Diğer bir örnek, uygulamanın diğer bölümlerinin veri deposuna doğrudan okuma veya yazma işlemleri yapamamasını sağlayan depo modelidir:

Ancak, mikro hizmetler mimarisinde, hizmetler aynı kod tabanını paylaşmaz ve veri depolarını paylaşmaz. Bunun yerine, API 'Ler üzerinden iletişim kurar. Zamanlayıcı hizmetinin drone hizmetinden bir DRA hakkında bilgi istemesi durumunda bu durumu göz önünde bulundurun. Drone hizmeti, kod aracılığıyla ifade edilen bir drone 'in iç modeline sahiptir. Ancak Zamanlayıcı bunu görmez. Bunun yerine, bir HTTP yanıtında bir JSON nesnesi olan drone varlığının bir gösterimini geri alır.

Zamanlayıcı hizmeti, drone hizmetinin iç modellerini değiştiremez veya drone hizmetinin veri deposuna yazamaz. Yani, drone hizmetini uygulayan kodun, geleneksel bir monode kodla karşılaştırıldığında daha küçük bir açık yüzey alanı vardır. Drone hizmeti bir konum sınıfını tanımlarsa, bu sınıfın kapsamı sınırlıdır; başka hiçbir hizmet doğrudan sınıfı tüketmez.
Bu nedenlerden dolayı, bu kılavuz, politik DDD desenleriyle ilgili kodlama uygulamalarına çok odaklanmaz. Ancak REST API 'Leri aracılığıyla birkaç DDD desenini de modelleyebilirsiniz.
Örnek:
Bağlantıları, bekleyen kaynaklara doğal olarak toplar. Örneğin, teslim toplama, teslim API 'SI tarafından kaynak olarak sunulur.
Toplamalar tutarlılık sınırlardır. Toplamalarda gerçekleştirilen işlemler asla tutarsız bir durumda hiçbir bir toplama ayrılmamalıdır. Bu nedenle, bir istemcinin bir toplamanın iç durumunu değiştirmesine izin veren API 'Ler oluşturmaktan kaçınmalısınız. Bunun yerine, toplamaları kaynak olarak kullanıma sunan kaba API 'Leri tercih edin.
Varlıkların benzersiz kimlikleri vardır. REST 'de kaynaklar URL biçiminde benzersiz tanımlayıcılara sahiptir. Bir varlığın etki alanı kimliğine karşılık gelen kaynak URL 'Leri oluşturun. URL 'den etki alanı kimliğine olan eşleme, istemciye opak olabilir.
Kök varlıktan gezinerek bir toplamanın alt varlıklara erişilebilir. Hateoas ilkeleri ' ni izlerseniz, alt varlıklara üst varlığın temsilindeki bağlantılar aracılığıyla ulaşılaneklenebilir.
Değer nesneleri sabit olduğundan, tüm değer nesnesi değiştirilerek güncelleştirmeler gerçekleştirilir. REST ' de, PUT veya PATCH istekleri aracılığıyla güncelleştirmeleri uygulayın.
Bir depo, istemcilerin bir koleksiyondaki nesneleri sorgulamasını, eklemesini veya kaldırmasını sağlar ve temel alınan veri deposunun ayrıntılarını soyutlar. Geri kalan bir koleksiyon, koleksiyonu sorgulama veya koleksiyona yeni varlıklar ekleme yöntemleriyle ayrı bir kaynak olabilir.
API 'lerinizi tasarlarken, yalnızca modelin içindeki verileri değil, aynı zamanda iş işlemlerini ve verilerdeki kısıtlamaları da alan modelini nasıl ifade ettikleri hakkında düşünün.
| DDD kavramı | REST eşdeğeri | Örnek |
|---|---|---|
| Toplama | Kaynak | { "1":1234, "status":"pending"... } |
| Kimlik | URL | https://delivery-service/deliveries/1 |
| Alt varlıklar | Bağlantılar | { "href": "/deliveries/1/confirmation" } |
| Değer nesnelerini Güncelleştir | PUT veya PATCH | PUT https://delivery-service/deliveries/1/dropoff |
| Depo | Koleksiyon | https://delivery-service/deliveries?status=pending |
API sürümü oluşturma
API, hizmet ve istemciler ile bu hizmetin tüketicileri arasında bir sözleşmedir. Bir API değişirse API 'ye bağlı olan istemcileri, bunların dış istemciler veya diğer mikro hizmetler olup olmadığı konusunda önemli bir risk vardır. Bu nedenle, yaptığınız API değişikliklerinin sayısını en aza indirmek iyi bir fikirdir. Genellikle, temel uygulamadaki değişiklikler API 'de herhangi bir değişiklik gerektirmez. Ancak, bazı bir noktada, var olan bir API 'nin değiştirilmesini gerektiren yeni özellikler veya yeni yetenekler eklemek isteyeceksiniz.
Mümkün olduğunda, API değişikliklerini geriye dönük olarak uyumlu hale getirin. Örneğin, bir alanı bir modelden kaldırmaktan kaçının, çünkü bu, alanı alan olmasını bekleyen istemcileri bozabilirler. Bir alan eklendiğinde uyumluluk kesintiye uğramaz, çünkü istemciler yanıt içinde anlamadığı alanları yoksaymalıdır. Ancak, hizmet, eski bir istemcinin bir istekteki yeni alanı atdığından bu durumu işlemelidir.
API sözleşmeniz üzerinde sürüm oluşturmayı destekler. Bir son API değişikliği belirtirseniz yeni bir API sürümü tanıtın. Önceki sürümü desteklemeye devam edin ve istemcilerin hangi sürümün çağrılacağını seçmesini sağlar. Bunu yapmak için birkaç yol vardır. Tek ikisi de aynı hizmette iki sürümü kullanıma sunmaktır. Diğer bir seçenek de hizmetin iki sürümünü yan yana çalıştırmak ve istekleri HTTP Yönlendirme kurallarına göre bir veya diğer sürüme yönlendirmenizi sağlar.
Diyagramda iki bölüm vardır. "Hizmet iki sürümü destekler", her ikisi de bir hizmete işaret eden v1 Istemcisini ve v2 Istemcisini gösterir. "Yan yana dağıtım" bir v1 hizmetine işaret eden v1 Istemcisini ve v2 hizmetini işaret eden v2 Istemcisini gösterir.
Geliştirici saati, test ve operasyonel ek yük açısından birden çok sürümü destekleme maliyeti vardır. Bu nedenle, eski sürümlerin mümkün olduğunca hızlı bir şekilde kaldırılması iyi olur. İç API 'Ler için, API 'ye sahip olan takım, diğer ekiplerle birlikte çalışarak yeni sürüme geçiş sağlanmasına yardımcı olabilir. Bu, bir çapraz takım idare süreci yararlı olduğunda yararlıdır. Dış (genel) API 'Ler için, özellikle de API üçüncü taraflar veya yerel istemci uygulamaları tarafından kullanılıyorsa bir API sürümünün kullanımdan kaldırılması daha zor olabilir.
Bir hizmet uygulamasında değişiklik yapıldığında, değişikliği bir sürümle etiketlemek yararlı olur. Sürüm, hata giderirken önemli bilgiler sağlar. Bu, kök neden analizi için hizmetin tam olarak hangi sürümünün çağrıldığını bilmekte çok yararlı olabilir. Hizmet sürümleri için anlamsal sürüm oluşturmayı kullanmayı düşünün. Anlamsal sürüm oluşturma büyük bir kullanır . Bazı. Düzeltme Eki biçimi. Ancak, istemciler ana sürüm numarası tarafından yalnızca bir API 'yi veya alt sürümler arasında önemli (ancak kırılmamış) değişiklikler varsa ikincil sürümü seçer. Diğer bir deyişle, istemcilerin sürüm 1 ile API sürüm 2 arasında seçim yapmak, ancak sürüm 2.1.3 ' i seçmemelidir. Bu düzeyde ayrıntı düzeyi için izin verirseniz, sürümlerin bir kısmını desteklemeye yönelik riski vardır.
API sürümü oluşturma hakkında daha fazla bilgi için bkz. Restilame Web API 'Sinin sürümü oluşturma.
Idempotent işlemleri
Bir işlem, ilk çağrıdan sonra ek yan etkiler üretmeden birden çok kez çağrılabilecek ıdempotent . Bir yukarı akış hizmetinin bir işlemi birden çok kez güvenli bir şekilde çağırmasına izin verdiğinden, ıdempotlik yararlı bir dayanıklılık stratejisi olabilir. Bu nokta hakkında bir tartışma için bkz. Dağıtılmış işlemler.
HTTP belirtimi GET, PUT ve DELETE yöntemlerinin ıdempotent olması gerektiğini belirtir. POST yöntemlerinin ıdempotent olduğu garanti edilmez. POST yöntemi yeni bir kaynak oluşturursa, genellikle bu işlemin ıdempotent olduğu garantisi yoktur. Belirtim şu şekilde ıdempotent tanımlar:
İstek yöntemi "ıdempotent" olarak kabul edilir. bu yöntemle birden fazla özdeş isteğin sunucuda amaçlanan etkisi, tek bir istek için etkiyle aynıdır. (RFC 7231)
Yeni bir varlık oluştururken PUT ve POST semantiği arasındaki farkı anlamak önemlidir. Her iki durumda da istemci, istek gövdesinde bir varlığın gösterimini gönderir. Ancak URI 'nin anlamı farklıdır.
POST yöntemi için URI, yeni varlığın bir koleksiyon gibi bir üst kaynağını temsil eder. Örneğin, yeni bir teslim oluşturmak için URI olabilir
/api/deliveries. Sunucu varlığı oluşturur ve bu varlığa yeni bir URI (gibi) atar/api/deliveries/39660. Bu URI, yanıtın konum üst bilgisinde döndürülür. İstemci bir istek gönderdiğinde, sunucu yeni bir URI ile yeni bir varlık oluşturur.PUT yöntemi için URI, varlığı tanımlar. Bu URI 'ye sahip bir varlık zaten varsa, sunucu mevcut varlığı istekteki sürümle değiştirir. Bu URI ile bir varlık yoksa, sunucu bir tane oluşturur. Örneğin, istemcisinin öğesine bir PUT isteği gönderdiğini varsayalım
api/deliveries/39660. Bu URI ile teslim olmadığı varsayılarak, sunucu yeni bir tane oluşturur. Artık istemci aynı isteği yeniden gönderirse, sunucu var olan varlığın yerini alır.
Teslim hizmetinin PUT yönteminin uygulanması aşağıda verilmiştir.
[HttpPut("{id}")]
[ProducesResponseType(typeof(Delivery), 201)]
[ProducesResponseType(typeof(void), 204)]
public async Task<IActionResult> Put([FromBody]Delivery delivery, string id)
{
logger.LogInformation("In Put action with delivery {Id}: {@DeliveryInfo}", id, delivery.ToLogInfo());
try
{
var internalDelivery = delivery.ToInternal();
// Create the new delivery entity.
await deliveryRepository.CreateAsync(internalDelivery);
// Create a delivery status event.
var deliveryStatusEvent = new DeliveryStatusEvent { DeliveryId = delivery.Id, Stage = DeliveryEventType.Created };
await deliveryStatusEventRepository.AddAsync(deliveryStatusEvent);
// Return HTTP 201 (Created)
return CreatedAtRoute("GetDelivery", new { id= delivery.Id }, delivery);
}
catch (DuplicateResourceException)
{
// This method is mainly used to create deliveries. If the delivery already exists then update it.
logger.LogInformation("Updating resource with delivery id: {DeliveryId}", id);
var internalDelivery = delivery.ToInternal();
await deliveryRepository.UpdateAsync(id, internalDelivery);
// Return HTTP 204 (No Content)
return NoContent();
}
}
Çoğu isteğin yeni bir varlık oluşturabilmesi, bu yüzden yöntemin CreateAsync Depo nesnesine kendini optimizasyonu ve sonra bunun yerine kaynağı güncelleştirerek yinelenen kaynak özel durumlarını işleymesidir.
Sonraki adımlar
İstemci uygulamaları ve mikro hizmetler arasındaki sınırda bir API ağ geçidi kullanma hakkında bilgi edinin.