Projektiranje API-ja za sustave koji se razvijaju: izvan brzog rješenja
API koji danas u jednom poslijepodnevu rješava zahtjev za integracijom može neprimjetno postati sutrašnja najskuplja ovisnost. To nije argument za pretjerano projektiranje. To je argument za prepoznavanje činjenice da je API ugovor: čim se druga aplikacija, tim ili kupac osloni na njega, njegova promjena postaje problem koordinacije, a ne zadatak refaktoriranja.
Dobra API arhitektura nije predviđanje svake buduće značajke. Riječ je o tome da uobičajena promjena bude sigurna. Najtrajniji sustavi uspostavljaju jasne granice, čuvaju značenje i ostavljaju prostor za evoluciju bez zarobljavanja svakog korisnika u sinkroniziranom ciklusu izdanja.
Počnite s ugovorom, a ne s kontrolerom
Uobičajeno brzo rješenje počinje postojećim krajnjim pristupom i novim poljem: dodajte stupac, izložite ga u JSON-u, ažurirajte kontroler i isporučite promjenu. To može biti sasvim primjereno za interni prototip. Postaje rizično kada polje mijenja značenje resursa, otkriva pojedinosti pohrane ili stvara ponašanje za koje će korisnici pretpostaviti da je trajno.
Definirajte ugovor u smislu poslovnih pojmova. Klijent bi trebao zatražiti narudžbu, status plaćanja ili dostupnu opciju dostave; ne bi trebao morati razumjeti nazive vaših tablica, ORM odnose ni redoslijed kojim se pozadinski zadaci slučajno izvršavaju.
Primjerice, odgovor može predstaviti stabilan oblik resursa, istodobno dopuštajući temeljnoj PHP aplikaciji da kasnije reorganizira usluge, redove i tablice baze podataka.
{
"id": "ord_4821",
"status": "processing",
"total": {
"amount": 4999,
"currency": "USD"
},
"links": {
"self": "/orders/ord_4821"
}
}
To ne znači da svaki odgovor treba hipermediju ili razrađen sloj apstrakcije. Znači da bi se korisnici trebali oslanjati na namjerna imena i dokumentiranu semantiku, a ne na slučajne pojedinosti implementacije poput order_total_cents ili interne enum vrijednosti koja bi sljedeći tjedan mogla biti preimenovana.
Najprije projektirajte za aditivne promjene
Najsigurnije API promjene su aditivne. Novo neobavezno polje, novi krajnji pristup ili dodatni filtar općenito se mogu uvesti bez narušavanja postojećih korisnika. Uklanjanje polja, promjena vrste polja, redefiniranje vrijednosti statusa ili postavljanje obaveznog polja zahtjeva koje je prije bilo neobavezno zahtijeva mnogo više opreza.
U praksi to vodi do korisnog zadanog pristupa: sačuvajte postojeće ponašanje i dodajte novu mogućnost uz njega. Ako polje niza znakova mora postati strukturirani podaci, nemojte mu neprimjetno promijeniti vrstu iz niza znakova u objekt. Uvedite novo polje, dokumentirajte oba tijekom prijelaznog razdoblja i uklonite staro samo kroz priopćen proces zastarijevanja.
- Koristite stabilne identifikatore koji nisu vezani uz strategiju primarnog ključa baze podataka.
- Učinite validaciju zahtjeva eksplicitnom, uključujući formate, ograničenja i dopuštene vrijednosti.
- Vraćajte predvidljive oblike pogrešaka kako bi klijenti mogli razlikovati neispravan unos od privremenog kvara.
- Dokumentirajte semantiku paginacije, sortiranja i filtriranja umjesto da ih ostavite kao zadane postavke kontrolera.
- Budite oprezni s „korisnim” zadanim postavkama; nedokumentirana zadana postavka i dalje je dio ugovora čim se klijenti oslone na nju.
Verzionirajte samo kada se ugovor doista razilazi
Verzioniranje je korisno, ali nije zamjena za disciplinu kompatibilnosti. Stvaranje nove verzije za svaku malu promjenu proizvodi duplicirani kôd, fragmentiranu dokumentaciju i korisnike koji se nikada ne nadograđuju. Potpuno izbjegavanje verzioniranja može klijentima nametnuti nekompatibilne promjene bez izlazne strategije.
Pragmatičan pristup jest zadržati stabilnu verziju dok promjene ostaju kompatibilne, a zatim uvesti novu glavnu verziju kada model resursa ili ponašanje uistinu ne mogu čisto koegzistirati. Konvencija temeljena na rutama kao što je /api/v1/orders jednostavna je za rad i nadzor mnogim timovima. Verzioniranje temeljeno na zaglavljima također može funkcionirati, ali često čini otklanjanje poteškoća i predmemoriranje manje očitima. Važna je dosljednost i realan životni ciklus starih verzija.
Zastarijevanje bi trebalo biti operativno, a ne ceremonijalno. Identificirajte aktivne korisnike gdje je to moguće, objavite cilj migracije, održavajte ponašanje determinističkim tijekom prijelaza i odredite datum uklanjanja samo kada ga organizacija može podržati. Stari krajnji pristup bez vlasništva ili plana povlačenja nije povratna kompatibilnost; to je trajni dug održavanja.
Učinite upise sigurnima pri ponovnim pokušajima
Mreže otkazuju na nezgodnim mjestima. Klijent može poslati zahtjev za stvaranje, izgubiti odgovor i pokušati ponovno. Ako krajnji pristup pri svakom ponovnom pokušaju stvori novo plaćanje, pošiljku ili narudžbu, API je tehnički dostupan, ali operativno nesiguran.
Za upise pokrenute izvana razmotrite ključeve idempotentnosti. Klijent pruža jedinstveni ključ za logičku operaciju, a poslužitelj pohranjuje ishod povezan s tim ključem. Ponovljeni zahtjev tada može vratiti izvorni rezultat umjesto da ponovno izvrši radnju.
$key = $request->header('Idempotency-Key');
if (!$key) {
return response()->json([
'error' => [
'code' => 'idempotency_key_required',
'message' => 'An Idempotency-Key header is required.'
]
], 400);
}
$existing = IdempotencyRecord::where('key', $key)->first();
if ($existing) {
return response()->json($existing->response_body, $existing->status_code);
}
Primjer je tek početak. Produkcijski dizajn također mora vezati ključ uz autentificiranog pozivatelja i operaciju, obraditi istodobne zahtjeve za isti ključ, definirati razdoblje čuvanja i pohraniti dovoljno podataka za otkrivanje ponovne upotrebe ključa s drukčijim sadržajem zahtjeva. U tim pojedinostima leži pouzdanost.
Neka baza podataka provodi važne istine
Validacija aplikacije poboljšava iskustvo klijenta, ali baza podataka trebala bi štititi invarijante koje se nikada ne smiju prekršiti. Ako adresa e-pošte mora biti jedinstvena unutar računa, koristite jedinstveno ograničenje. Ako stavka narudžbe mora pripadati narudžbi, koristite strani ključ tamo gdje to podatkovni model dopušta. Ako prijelaz stanja zahtijeva isključivi pristup, oblikujte taj zahtjev transakcijama, zaključavanjem ili optimističkom konkurentnošću, prema potrebi.
API-ji često otkrivaju probleme konkurentnosti koji su bili nevidljivi u sučelju za jednog korisnika. Dva radnika mogu ažurirati isti resurs, webhook može stići dok korisnik mijenja postavke, a asinkroni zadatak može završiti nakon isteka vremenskog ograničenja. Razmotrite vraćanje oznake verzije poput updated_at ili broja revizije te zahtijevanje da ga klijent pošalje za osjetljiva ažuriranja. Nepodudaranje može vratiti odgovor o sukobu umjesto da neprimjetno prepiše novije podatke.
Neka pozadinski slojevi budu korisni, a ne ceremonijalni
PHP okviri olakšavaju smještanje usmjeravanja, validacije, autorizacije, pohrane i oblikovanja odgovora u jednu metodu kontrolera. Ta pogodnost kratko traje. Kako se pravila umnožavaju, akcije kontrolera postaju teške za testiranje i lako ih je narušiti.
Struktura koja se može održavati obično odvaja HTTP pitanja od ponašanja aplikacije. Kontroleri prevode zahtjeve i odgovore. Objekti zahtjeva validiraju unos. Aplikacijske usluge koordiniraju slučajeve upotrebe. Kôd usmjeren na domenu upravlja poslovnim pravilima. Repozitoriji ili usluge upita mogu izdvojiti složenu perzistenciju tamo gdje to izdvajanje pruža stvarnu vrijednost.
Nemojte stvarati slojeve samo zato što dijagram kaže da im je ondje mjesto. Mali krajnji pristup s jednostavnim upitom ne treba šest klasa. Izdvojite granicu kada štiti pravilo, smanjuje dupliciranje ili omogućuje zamjenu nestabilne ovisnosti. Arhitektura bi trebala smanjiti trošak promjene, a ne povećati broj datoteka potrebnih za jednu promjenu.
Uvodite promjene kao slijed, a ne kao događaj
Promjene sheme zaslužuju isti način razmišljanja o kompatibilnosti kao i API promjene. Implementacija koja dodaje stupac koji ne dopušta null vrijednosti, odmah u njega zapisuje i pretpostavlja da svaki spremnik aplikacije ima novi kôd može zakazati tijekom postupnih implementacija. Sigurniji je obrazac proširi, migriraj, prebaci i smanji.
- Dodajte novu shemu u obliku koji je povratno kompatibilan.
- Implementirajte kôd koji može čitati i zapisivati i staru i novu reprezentaciju.
- Popunite postojeće podatke u kontroliranim serijama.
- Nakon provjere prebacite čitanja na novu reprezentaciju.
- Uklonite zastarjeli kôd i shemu tek nakon prijelaznog razdoblja.
Docker i automatizirani cjevovodi implementacije čine ovaj slijed ponovljivim, ali ne uklanjaju potrebu za njim. Pokrećite migracije promišljeno, učinite provjere stanja smislenima i osigurajte da instance aplikacije mogu kratko koegzistirati između verzija implementacije.
Trajno mjerilo API-ja
API je uspješan kada promjena ostaje dosadna. Korisnici se mogu nadograditi bez straha, operateri mogu dijagnosticirati kvarove na temelju jasnih signala, a pozadinski inženjeri mogu poboljšavati unutarnje dijelove bez izlaganja svake odluke implementacije vanjskom svijetu.
Brzo rješenje ponekad je pravi prvi potez. Potez iskusnog inženjera jest znati koji prečaci postaju ugovori, a zatim oko njih postaviti upravo dovoljno strukture da sljedeća promjena bude sigurnija od prethodne.