Návrh rozhraní API pro mikroslužby
Dobrý návrh rozhraní API je důležitý v architektuře mikroslužeb, protože veškerá výměna dat mezi službami probíhá buď prostřednictvím zpráv, nebo volání rozhraní API. Rozhraní API musí být efektivní, aby se zabránilo vytváření přemostěných V/V . Vzhledem k tomu, že služby jsou navržené týmy, které pracují nezávisle, rozhraní API musí mít dobře definovaná schémata sémantiky a verzí, aby aktualizace neporušily jiné služby.

Je důležité rozlišovat mezi dvěma typy rozhraní API:
- Veřejná rozhraní API, která volají klientské aplikace.
- Back-endová rozhraní API, která se používají pro komunikaci mezi službami.
Tyto dva případy použití mají poněkud odlišné požadavky. Veřejné rozhraní API musí být kompatibilní s klientskými aplikacemi, obvykle s aplikacemi v prohlížeči nebo nativními mobilními aplikacemi. Ve většině času to znamená, že veřejné rozhraní API bude používat REST přes HTTP. U rozhraní API back-endu ale musíte vzít v úvahu výkon sítě. V závislosti na členitosti služeb může komunikace mezi službami vést k mnoha síťovým přenosům. Služby se mohou rychle stát vázaná na V/V. Z tohoto důvodu jsou důležité aspekty, jako je rychlost serializace a velikost datové části. Mezi oblíbené alternativy použití REST přes HTTP patří gRPC, Apache Avro a Apache Thrift. Tyto protokoly podporují binární serializaci a jsou obecně efektivnější než HTTP.
Požadavky
Tady je několik věcí, na které byste se měli zamyslet při výběru způsobu implementace rozhraní API.
REST versus RPC. Zvažte kompromisy mezi použitím rozhraní ve stylu REST a rozhraním ve stylu RPC.
Prostředky modelů REST, což může být přirozený způsob vyjádření doménového modelu. Definuje jednotné rozhraní založené na příkazech HTTP, což podporuje vhodnost. Má dobře definovanou sémantiku z hlediska idempotence, vedlejších účinků a kódů odpovědí. A vynucuje bezvýcovou komunikaci, což zlepšuje škálovatelnost.
RPC se více orientuje na operace nebo příkazy. Vzhledem k tomu, že rozhraní RPC vypadají jako volání místní metody, může vás to vést k návrhu rozhraní API, která jsou přemostěna. To ale neznamená, že rpc musí být chatovitá. Znamená to jen, že při navrhování rozhraní musíte být opatrně.
V případě rozhraní RESTful je nejběžnější volbou REST přes HTTP využívající JSON. Pro rozhraní ve stylu RPC existuje několik oblíbených rozhraní, včetně gRPC, Apache Avro a Apache Thrift.
Efektivita. Zvažte efektivitu z hlediska rychlosti, paměti a velikosti datové části. Rozhraní založené na gRPC je obvykle rychlejší než rozhraní REST přes HTTP.
Jazyk IDL (Interface Definition Language). IDL slouží k definování metod, parametrů a návratových hodnot rozhraní API. IDL lze použít ke generování kódu klienta, serializace kódu a dokumentaci rozhraní API. Knihovny IDL je možné využívat také pomocí testovacích nástrojů rozhraní API, jako je Postman. Architektury jako gRPC, Avro a Thrift definují vlastní specifikace IDL. REST přes HTTP nemá standardní formát IDL, ale běžnou volbou je OpenAPI (dříve Swagger). Můžete také vytvořit protokol HTTP bez REST API jazyka formální definice, ale pak ztratíte výhody generování kódu a testování.
Serializace. Jak jsou objekty serializované přes drátové připojení? Mezi možnosti patří textové formáty (primárně JSON) a binární formáty, jako je vyrovnávací paměť protokolu. Binární formáty jsou obvykle rychlejší než textové formáty. Json ale má výhody z hlediska interoperability, protože většina jazyků a architektur podporuje serializaci JSON. Některé formáty serializace vyžadují pevné schéma a některé vyžadují kompilaci definiční soubor schématu. V takovém případě budete muset tento krok začlenit do procesu sestavení.
Podpora architektury a jazyka HTTP se podporuje téměř ve všech architektur a jazycích. GRPC, Avro a Thrift mají knihovny pro C++, C#, Javu a Python. Thrift a gRPC také podporují Go.
Kompatibilita a interoperabilita. Pokud zvolíte protokol, jako je gRPC, možná budete potřebovat vrstvu překladu protokolů mezi veřejným rozhraním API a back-endem. Brána může tuto funkci provést. Pokud používáte síť služeb, zvažte, které protokoly jsou kompatibilní se sítí služeb. Linkerd má například integrovanou podporu pro HTTP, Thrift a gRPC.
Naše základní doporučení je zvolit REST přes HTTP, pokud nepotřebujete výhody výkonu binárního protokolu. REST přes HTTP nevyžaduje žádné speciální knihovny. Vytvoří minimální párování, protože volající nepožádá o zástupné procedury klienta pro komunikaci se službou. K dispozici jsou bohaté ekosystémy nástrojů pro podporu definic schémat, testování a monitorování koncových bodů HTTP RESTful. Protokol HTTP je kompatibilní s klienty prohlížeče, takže nepotřebujete vrstvu překladu protokolu mezi klientem a back-endem.
Pokud ale zvolíte REST přes HTTP, měli byste v rané fázi procesu vývoje provést testování výkonu a zatížení, abyste ověřili, jestli je pro váš scénář dostatečně dobrý.
Návrh rozhraní RESTful API
Pro návrh rozhraní RESTful API existuje mnoho zdrojů informací. Tady jsou některé z vás, které vám můžou pomoct:
Tady je několik specifických požadavků, které je třeba mít na paměti.
Dávejte pozor na rozhraní API, která prozrazení podrobností o interní implementaci nebo jednoduše zrcadlí schéma interní databáze. Rozhraní API by mělo modelovat doménu. Jedná se o smlouvu mezi službami a v ideálním případě by se měla změnit pouze při přidání nových funkcí, nejen proto, že jste refaktoroval nějaký kód nebo normalizovali databázová tabulka.
Různé typy klientů, jako jsou mobilní aplikace a desktopový webový prohlížeč, mohou vyžadovat různé velikosti datové části nebo vzory interakce. Zvažte použití modelu Back-endy pro front-endy k vytvoření samostatných back-endů pro každého klienta, které zpřístupní optimální rozhraní pro tohoto klienta.
U operací s vedlejšími účinky zvažte jejich idempotentní a implementaci jako metod PUT. To umožní bezpečné opakování a může zlepšit odolnost. Tento problém se podrobněji popisuje v článku Komunikace mezi službami.
Metody HTTP mohou mít asynchronní sémantiku, kdy metoda vrátí odpověď okamžitě, ale služba provádí operaci asynchronně. V takovém případě by metoda měla vrátit kód odpovědi HTTP 202, který indikuje, že požadavek byl přijat ke zpracování, ale zpracování ještě není dokončeno. Další informace najdete v tématu Asynchronní Request-Reply modelu.
Mapování REST na vzory DDD
Vzory, jako je entita, agregace a objekt hodnoty, jsou navržené tak, aby u objektů v doménovém modelu umistly určitá omezení. V mnoha diskuzích o DDD jsou vzory modelovány pomocí konceptů jazyka objektově orientovaného (OO), jako jsou konstruktory nebo funkce getter a setery vlastností. Objekty hodnot by například měly být neměnné. V programovacím jazyce OO byste to vynucovat přiřazením hodnot v konstruktoru a vytvořením vlastností jen pro čtení:
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;
}
}
Tyto druhy postupů kódování jsou obzvláště důležité při vytváření tradiční monolitické aplikace. V případě velkého základu kódu může objekt používat mnoho subsystémů, takže je důležité, aby objekt Location vynucuje správné chování.
Dalším příkladem je model Úložiště, který zajišťuje, že jiné části aplikace nebudou provádět přímé čtení ani zápisy do úložiště dat:

V architektuře mikroslužeb ale služby nesdílejte stejný základ kódu a nesdílejte úložiště dat. Místo toho komunikují prostřednictvím rozhraní API. Zamyslete se nad případem, kdy služba Scheduler požaduje informace o dronech ze služby Drone. Služba Drone má svůj interní model dronu vyjádřený prostřednictvím kódu. Plánovač to ale nevidí. Místo toho získá zpět reprezentaci entity dronu, pravděpodobně objektu — JSON v odpovědi HTTP.

Služba plánovače nemůže měnit interní modely služby pomocí dronů ani zapisovat do úložiště dat služby pomocí dronů. To znamená, že kód, který implementuje službu pomocí dronů, má menší vystavenou plochu v porovnání s kódem v tradičním monolitu. Pokud služba pomocí dronů definuje třídu umístění, není rozsah této třídy omezen — žádnou jinou službou, která bude přímo spotřebovávat třídu.
Z těchto důvodů se tyto pokyny nezaměřují na postupy kódování, protože se vztahují ke vzorům taktické DDD. Ale zapíná, že můžete také modelovat mnoho vzorů DDD prostřednictvím rozhraní REST API.
Například:
Agreguje mapu přirozeně do prostředků v klidovém umístění. Rozhraní API pro doručování může například vystavit agregované doručování jako prostředek.
Agregace jsou hranice konzistence. Operace na agregacích by nikdy neměly zůstat agregované v nekonzistentním stavu. Proto byste se měli vyhnout vytváření rozhraní API, které umožňuje klientovi manipulovat s vnitřním stavem agregace. Místo toho upřednostnit hrubá rozhraní API, která zveřejňují agregace jako prostředky.
Entity mají jedinečné identity. V REST mají prostředky jedinečné identifikátory v podobě adres URL. Vytvořte adresy URL prostředků, které odpovídají identitě domény entity. Mapování adresy URL na identitu domény může být neprůhledné od klienta.
Podřízené entity agregace lze dosáhnout přechodem z kořenové entity. Pokud budete postupovat podle HATEOASch principů, podřízené entity je možné dosáhnout prostřednictvím odkazů v zastoupení nadřazené entity.
Vzhledem k tomu, že objekty hodnot jsou neměnné, jsou aktualizace provedeny nahrazením celého objektu Value. V REST implementujte aktualizace prostřednictvím požadavků PUT nebo PATCH.
Úložiště umožňuje klientům dotazovat, přidávat nebo odebírat objekty v kolekci a abstrakce podrobnosti základního úložiště dat. V REST může být kolekce samostatným prostředkem s metodami pro dotazování kolekce nebo přidávání nových entit do kolekce.
Když navrhujete rozhraní API, zamyslete se nad tím, jak vyjadřují doménový model, nejen data v modelu, ale i obchodní operace a omezení pro data.
| DDD koncept | Ekvivalent REST | Příklad |
|---|---|---|
| Agregace | Prostředek | { "1":1234, "status":"pending"... } |
| Identita | URL | https://delivery-service/deliveries/1 |
| Podřízené entity | Odkazy | { "href": "/deliveries/1/confirmation" } |
| Aktualizovat objekty hodnot | Vložit nebo opravit | PUT https://delivery-service/deliveries/1/dropoff |
| Repository | Kolekce | https://delivery-service/deliveries?status=pending |
Správa verzí rozhraní API
Rozhraní API je smlouva mezi službou a klienty nebo spotřebiteli této služby. Pokud se změní rozhraní API, existuje riziko přerušujících klientů závislých na rozhraní API, ať už jde o externí klienty nebo jiné mikroslužby. Proto je vhodné minimalizovat počet změn rozhraní API, které provedete. Změny v základní implementaci často nevyžadují žádné změny v rozhraní API. V některých případech ale budete chtít přidat nové funkce, které vyžadují změnu existujícího rozhraní API, a to v nějakém bodě.
Kdykoli je to možné, je změna rozhraní API zpětně kompatibilní. Neodstraňujte například pole z modelu, protože to může přerušit klienty, kteří očekávají, že pole je. Přidáním pole nedojde k přerušení kompatibility, protože klienti by měli ignorovat všechna pole, která v odpovědi nerozumí. Služba však musí pocházet s případem, kdy starší klient vynechává nové pole v žádosti.
Podpora správy verzí v kontraktu rozhraní API. Pokud zavádíte průlomovou změnu rozhraní API, zaveďte novou verzi rozhraní API. Pokračujte na podporu předchozí verze a umožněte klientům vybrat, která verze se má zavolat. To lze provést několika způsoby. Jedna je jednoduše k zveřejnění obou verzí ve stejné službě. Další možností je spustit dvě verze služby souběžně a směrovat požadavky na jednu nebo druhou verzi na základě pravidel směrování HTTP.
Diagram má dvě části. Služba podporuje dvě verze – klient V1 a klient verze 2 odkazuje na jednu službu. U souběžného nasazení se zobrazuje klient v1 ukazující na službu V1 a klient v2 odkazuje na službu v2.
Existují náklady na podporu více verzí, a to z pohledu vývoje, testování a provozní režie. Proto je dobré vyřadit starší verze co nejrychlejší. V případě interních rozhraní API může tým, který vlastní rozhraní API, pracovat s ostatními týmy, aby je mohl migrovat na novou verzi. To je užitečné v případě, že je k spolupráci proces zásad správného řízení mezi týmy. U externích (veřejných) rozhraní API může být obtížnější vyřadit verzi rozhraní API, obzvláště pokud rozhraní API spotřebovávají třetí strany nebo nativní klientské aplikace.
V případě změny implementace služby je vhodné označit změnu ve verzi. Verze poskytuje důležité informace při řešení chyb. Analýza hlavních příčin může být velmi užitečná, aby dokázala přesně zjistit, která verze služby byla volána. Zvažte použití sémantické správy verzí pro verze služeb. Sémantická verze používá hlavní. Moll. Formát opravy . Klienti by však měli vybrat rozhraní API pouze podle hlavní verze, případně z dílčí verze, pokud existují významné (ale nepatrné) změny mezi podverzemi. Jinými slovy je vhodné, aby klienti vybrali mezi verzí 1 a 2 rozhraní API, ale ne k výběru verze 2.1.3. Pokud povolíte tuto úroveň členitosti, riskujete, že bude nutné podporovat šíření verzí.
Další diskuzi o verzích API najdete v tématu Správa verzí RESTful webového rozhraní API.
Operace idempotentní
Operace je idempotentní , pokud ji lze volat vícekrát, aniž by bylo nutné po prvním volání vytvářet další vedlejší účinky. Idempotence může být užitečnou strategií odolnosti proti chybám, protože umožňuje nadřazené službě bezpečně vyvolat operaci víckrát. Diskuzi o tomto bodu najdete v tématu distribuované transakce.
Specifikace protokolu HTTP, které metody GET, PUT a DELETE, musí být idempotentní. Metody POST nejsou zaručené idempotentní. Pokud metoda POST vytvoří nový prostředek, obecně není nijak zaručena, že tato operace je idempotentní. Specifikace definuje idempotentní tímto způsobem:
Metoda žádosti se považuje za "idempotentní", pokud je zamýšlený účinek na server několika identických žádostí s touto metodou stejný jako účinek pro jednu takovou žádost. (RFC 7231)
Při vytváření nové entity je důležité pochopit rozdíl mezi sémantikou PUT a POST. V obou případech klient pošle v textu požadavku reprezentaci entity. Ale význam identifikátoru URI je jiný.
Pro metodu POST identifikátor URI představuje nadřazený prostředek nové entity, jako je například kolekce. Chcete-li například vytvořit nové doručování, může být identifikátor URI
/api/deliveries. Server vytvoří entitu a přiřadí jí nový identifikátor URI, například/api/deliveries/39660. Tento identifikátor URI se vrátí v hlavičce umístění odpovědi. Pokaždé, když klient odešle žádost, vytvoří server novou entitu s novým identifikátorem URI.V případě metody PUT identifikátor URI identifikuje entitu. Pokud již existuje entita s tímto identifikátorem URI, server nahradí existující entitu verzí v žádosti. Pokud s tímto identifikátorem URI neexistuje žádná entita, server ho vytvoří. Předpokládejme například, že klient odesílá požadavek PUT na
api/deliveries/39660. Za předpokladu, že se s tímto identifikátorem URI neprovádí žádné doručování, Server vytvoří nové. Když teď klient pošle stejnou žádost znovu, server nahradí existující entitu.
Zde je implementace metody PUT služby doručování.
[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čekává se, že většina požadavků vytvoří novou entitu, takže metoda optimisticky volá CreateAsync objekt úložiště a pak zpracovává všechny výjimky duplicitních prostředků tím, že místo toho provede aktualizaci prostředku.
Další kroky
Přečtěte si o používání brány rozhraní API na hranici mezi klientskými aplikacemi a mikroslužbami.