Dizajniranje API-ja koji se lako održavaju: Umijeće pripreme za budućnost
API je obećanje dano u kodu. Jednom kada se na njega oslanja druga usluga, mobilna aplikacija, partner ili frontend, svako ime polja, statusni kod i rubni slučaj postaju dio tog obećanja. Težak dio nije izlaganje podataka. Težak dio je očuvanje jasnoće dok se zahtjevi, timovi i sustavi neizbježno mijenjaju.
Zaštita od budućnosti ne znači predviđanje svakog zahtjeva za značajkom. Znači dizajniranje granica koje mogu apsorbirati razumnu promjenu bez prisiljavanja svakog korisnika na reakciju. API-ji koji se mogu održavati čine uobičajeni put očitim, kvarove razumljivima, a kasnije odluke jeftinijima.
Počnite sa stabilnim konceptima, ne s tablicama baze podataka
Uobičaveni prečac je pretvaranje redaka baze podataka izravno u JSON odgovore. Osjeća se učinkovito dok se shema ne promijeni, stupac nema nesretno ime ili se interni podaci ne moraju kombinirati u korisniji resurs. U tom trenutku, API je slučajno postao javni adapter baze podataka.
Modelirajte API oko koncepata koje klijenti trebaju. Korisnički naručeni resurs može uključivati identifikator, status, ukupne iznose i stavke, čak i ako te vrijednosti dolaze iz nekoliko tablica ili usluga. Interni raspored pohrane trebao bi ostati slobodan za evoluciju.
Ova je separacija posebno vrijedna u PHP aplikacijama, gdje ORM model može biti zgodan, ali ne bi trebao automatski definirati oblik odgovora. Koristite eksplicitne transformere resursa, DTO-e ili serijalizatore. Oni čine ugovor vidljivim u kodu i pružaju jedno mjesto za rukovanje formatiranjem, izostavljenim poljima i odlukama o kompatibilnosti.
function orderResponse(Order $order): array
{
return [
'id' => (string) $order->id,
'status' => $order->status,
'total' => [
'amount' => $order->totalAmount,
'currency' => $order->currency,
],
];
}
Taj mali sloj može spriječiti da buduća migracija postane događaj koji lomi API.
Učinite ugovore dovoljno preciznima da im se može vjerovati
Korisnici ne bi trebali morati zaključivati ponašanje pokusima i pogreškama. Definirajte što polje znači, je li dopušteno da bude odsutno ili null, koje su vrijednosti dopuštene i što se događa kada zahtjev ne uspije. Dosljednost je važnija od stilskog savršenstva.
Na primjer, odaberite jednu reprezentaciju za vremenske oznake i koristite je svugdje. Odlučite je li opcionalno polje izostavljeno ili vraćeno kao null, zatim dokumentirajte i sačuvajte taj izbor. Dosljedno koristite paginaciju na svim kolekcijskim krajnjim točkama. Klijent koji se može osloniti na poznate obrasce lakše je izraditi i sigurnije ga održavati.
Koristite pogreške kao dio proizvoda
Odgovori na pogreške često se dizajniraju posljednji, unatoč tome što su jedan od najčešće korištenih dijelova API-ja tijekom integracije i rada. Korisna pogreška govori klijentu što je pošlo po zlu, gdje je to praktično, i hoće li ponovni pokušaj pomoći.
{
"error": {
"code": "validation_failed",
"message": "Jedno ili više polja je nevažeće.",
"details": {
"email": ["Mora biti važeća adresa e-pošte."]
}
}
}
Neka poruke sigurne za klijente budu odvojene od interne dijagnostike. Produkcijski odgovor ne bi trebao izlagati SQL izjave, tragove poziva, vjerodajnice ili detalje implementacije. Zapišite tehnički kontekst interno, idealno s identifikatorom zahtjeva ili korelacijskim identifikatorom koji osoblje podrške može koristiti za praćenje događaja.
Dizajnirajte za aditivne promjene
Najsigurnije promjene API-ja su aditivne. Dodavanje opcionalnog polja obično je lakše za klijente tolerirati nego preimenovanje polja, promjena tipa ili mijenjanje značenja postojećeg. "Obično" je važno: neki strogi klijentski validatori odbacuju nepoznate svojstva, pa važne integracije i dalje zaslužuju testiranje ugovora i jasnu komunikaciju o izdanju.
Prije promjene utvrđene krajnje točke, postavite praktično pitanje: može li se novo ponašanje neko vrijeme odvijati uz staro ponašanje? Često može.
- Dodajte novo polje umjesto ponovne upotrebe starog.
- Uvedite novu krajnju točku kada je značenje resursa istinski promijenjeno.
- Podržite eksplicitnu verziju samo kada se kompatibilnost ne može čisto očuvati.
- Postavite i komunicirajte vremenski okvir ukidanja prije uklanjanja podržanog ugovora.
Verzioniranje je korisno, ali nije zamjena za pažljiv dizajn. Nova verzija za svaku malu prilagodbu stvara paralelne sustave koji se svi moraju dokumentirati, osigurati, nadzirati i na kraju ukinuti. Preferirajte stabilne semantike i aditivnu evoluciju; velike verzije rezervirajte za značajne nekompatibilnosti.
Odvojite transport, domensku logiku i perzistenciju
Održivost brzo opada kada kontroler validira ulaz, provodi poslovna pravila, upisuje u bazu podataka, poziva usluge trećih strana, formatira odgovor i rukuje ponovnim pokušajima u jednoj metodi. Ta struktura može funkcionirati za ranu krajnju točku, ali čini kasnije promjene rizičnima jer su zabrinutosti isprepletene.
Zdraviji protok pozadinskog sustava je jednostavan: validirajte i autorizirajte na granici, predajte normaliziranu naredbu aplikacijskoj ili domenskoj logici, pohranite kroz usredotočeni infrastrukturni kod i formatirajte izlaz na rubu. Točna arhitektura može varirati. Ključ je da svaki sloj ima jasnu odgovornost.
Transakcije baze podataka zaslužuju istu disciplinu. Neka budu uske i izbjegavajte sporije mrežne pozive dok je transakcija otvorena. Ako operacija mora pokrenuti vanjski sporedni efekt, transakcijski zabilježite namjeravani događaj i obradite ga odvojeno. Ovo smanjuje šansu da klijentu kažete da je akcija uspjela dok se baza podataka i sustav nizvodno ne slažu.
Performanse su briga o ugovoru
API koji je ispravan, ali nepredvidivo spor, teško je na njega se osloniti. Rad na performansama trebao bi započeti razumnim oblicima odgovora i ponašanjem upita, a ne preuranjenim predmemoriranjem.
Kolekcijske krajnje točke trebaju paginaciju, ograničene filtre i namjerno sortiranje. Izbjegavajte učitavanje cijelih povezanih grafova prema zadanim postavkama. U PHP aplikacijama koje koriste relacijske baze podataka, pazite na N+1 upite: popis se može činiti brzim u razvoju, ali izdaje jedan dodatni upit za svaku vraćenu stavku pod realističnim prometom. Učitajte potrebne odnose namjerno, izmjerite upite i vratite samo ono što krajnja točka obećaje.
Predmemoriranje može pomoći, ali dodaje pravila poništavanja i dosljednosti. Koristite ga tamo gdje se podaci često čitaju, mijenjaju predvidljivo, a ponašanje zastarjelih odgovora je prihvatljivo. Predmemorija bez strategije vlasništva i isteka nije samo optimizacija; to je još jedan sustav za upravljanje.
Operativni dizajn pripada diskusiji o API-ju
Implementacija ne smije slučajno promijeniti ponašanje API-ja. Kontejnerizirane usluge imaju koristi od eksplicitne konfiguracije, nepromjenjivih artefakata izrade, provjera ispravnosti koje odražavaju spremnost i tajni specifičnih za okruženje koji se dostavljaju izvan slike. Docker čini pakiranje ponovljivim, ali ne uklanja potrebu za planiranjem migracije baze podataka, strategijom vraćanja i promatranjem.
Za krajnje točke za pisanje, razmislite o vremenskim ograničenjima i ponovnim pokušajima. Klijenti, proxyji i radnici na poslovima mogu ponovno pokušati zahtjev. Ako stvaranje plaćanja, narudžbe ili računa nije idempotentno, privremeni kvar može stvoriti duplicirani posao. Gdje je prikladno, prihvatite ključ za idempotentnost, pohranite njegov ishod i vratite izvorni rezultat za ponovljeni zahtjev.
Konačno, testirajte ugovor, ne samo implementaciju. Jedinični testovi štite poslovna pravila; integracijski testovi provjeravaju ponašanje baze podataka i okvira; testovi ugovora provjeravaju primaju li klijenti polja, statusne kodove i pogreške koje očekuju. Ovi testovi pretvaraju kompatibilnost iz nade u provjereno svojstvo svakog izdanja.
Dugoročni pogled
API-ji koji se mogu održavati rijetko su najpametniji. Oni su oni čija se pravila mogu jasno objasniti, čiji se kvarovi mogu mirno dijagnosticirati, a čija sljedeća promjena ne zahtijeva krizni sastanak. Tretirajte API kao trajni granični proizvod, a ne kao tanku rutu do tablice. Taj način razmišljanja ostavlja prostora za rast sustava bez da svaki korisnik plaća za njegov rast.