Isporučujte robusne API-je: nadiđite okvire za trajnu arhitekturu
Okviri su izvrsni za brzo pokretanje API-ja. Pružaju usmjeravanje, pomoćne alate za validaciju, ubrizgavanje ovisnosti, migracije, redove i poznatu strukturu za novu uslugu. No okvir ne može odlučiti gdje pripadaju poslovna pravila, što se događa kada pružatelj platnih usluga istekne u vremenu ili može li se promjena baze podataka sigurno postaviti dok starije instance aplikacije još rade.
Robusni API-ji nastaju iz arhitektonskih odluka koje ostaju smislene nakon prvog izdanja: jasnih granica, eksplicitnog rukovanja greškama, sigurnih promjena podataka i operativnih navika koje ponašanje čine razumljivim pod pritiskom. PHP može vrlo dobro podržati takav stil inženjerstva, pod uvjetom da se okvir tretira kao mehanizam isporuke, a ne kao sama arhitektura.
HTTP sloj neka bude namjerno tanak
HTTP kontroler trebao bi prevesti zahtjev u radnju aplikacije i rezultat ponovno prevesti u HTTP odgovor. Ne bi smio postati mjesto na kojem se nakupljaju autorizacija, određivanje cijena, perzistencija, obavijesti i pozivi trećim stranama.
Korisna granica jest odvajanje transportnih aspekata od slučajeva upotrebe aplikacije. Kontroler parsira i validira ulaz. Usluga slučaja upotrebe koordinira rad. Objekti usmjereni na domenu sadržavaju pravila koja moraju ostati istinita neovisno o tome je li pozivatelj HTTP, CLI naredba, radnik reda ili zakazani zadatak.
final class CreateOrderController
{
public function __invoke(CreateOrderRequest $request, CreateOrder $useCase): JsonResponse
{
$order = $useCase->handle(
new CreateOrderInput(
customerId: $request->user()->id,
items: $request->validated('items')
)
);
return response()->json(['id' => $order->id], 201);
}
}
Ovo nije arhitektura radi arhitekture. Kada se pravila narudžbe promijene, imaju jedno prirodno mjesto. Kada drugo sučelje treba stvoriti narudžbu, može ponovno upotrijebiti isti slučaj upotrebe bez simuliranja HTTP zahtjeva. Testovi također postaju brži i fokusiraniji jer većina poslovnog ponašanja ne zahtijeva web-poslužitelj.
Učinite granice vidljivima u kodu
„Usluga” je često nejasna oznaka. Dajte prednost nazivima koji otkrivaju odgovornost: CreateInvoice, CalculateTax, CustomerRepository ili PaymentGateway. Važan dio nije slijediti modernu strukturu mapa; važno je da ovisnosti upućuju u smislenom smjeru.
Poslovna pravila ne bi trebala znati dolaze li podaci iz MySQL-a, PostgreSQL-a, Redisa ili vanjskog API-ja. S druge strane, infrastrukturni kod ne bi smio potajno redefinirati poslovne odluke. Prilagodnik pristupnika za plaćanja može znati kako poslati HTTP zahtjev. Aplikacijski sloj odlučuje kada treba pokušati izvršiti plaćanje i što odbijeno plaćanje znači za tijek rada.
Sučelja su najvrjednija na stvarnim granicama: vanjskim uslugama, vremenu, slučajnosti, pohrani datoteka ili složenoj perzistenciji. Stvaranje sučelja za svaku klasu dodaje formalnost bez poboljšanja mogućnosti izmjene. Počnite s konkretnim kodom ondje gdje je granica lokalna, a zatim uvedite apstrakciju kada je opravdavaju višestruke implementacije ili izolirano testiranje.
Dizajnirajte za kvar prije nego što ga promet pronađe umjesto vas
Svaka udaljena ovisnost može zakazati, odgovoriti sa zakašnjenjem ili uspjeti nakon što je vaš klijent odustao od čekanja. Pouzdan API definira odgovor na takva stanja umjesto da prepusti zadanim postavkama da odluče.
- Postavite vremenska ograničenja. Odlazni zahtjev bez vremenskog ograničenja može neograničeno zauzimati kapacitet radnika.
- Pokušavajte ponovno selektivno. Ponovite prolazne kvarove poput pogrešaka veze ili određenih pogrešaka poslužitelja, a ne pogreške validacije ili svaki odgovor koji nije uspješan.
- Koristite idempotentnost za zahtjeve koji mijenjaju stanje. Ponovni pokušaj klijenta ne smije stvoriti dvije narudžbe zato što je prvi odgovor izgubljen.
- Odvojite trajni rad od neposrednih odgovora. Šaljite e-poštu, generirajte izvješća ili obavještavajte integracije asinkrono kada korisniku rezultat nije potreban odmah.
- Zabilježite dovoljno konteksta za istragu. ID-jevi korelacije, stabilni kodovi pogrešaka i strukturirani zapisi korisniji su od općenitog „nešto je pošlo po zlu”.
Redovi pomažu, ali nisu čarobni prekidač za pouzdanost. Posao u redu može se izvršiti dvaput, stići kasno ili trajno zakazati. Rukovatelji bi stoga, gdje je moguće, trebali biti sigurni za ponavljanje. Na primjer, pohranite identifikator događaja pružatelja prije primjene učinka webhooka i odbijte duplikate jedinstvenim ograničenjem baze podataka. Neka baza podataka provodi invarijantu umjesto da se oslanjate isključivo na memoriju aplikacije.
Vratite korisne pogreške bez otkrivanja internih detalja
Klijentima su potrebne predvidljive pogreške; napadačima nisu potrebni zapisi stoga. Definirajte stabilan oblik pogreške sa strojno čitljivim kodom, ljudima čitljivom porukom i neobaveznim detaljima polja. Interno zabilježite iznimku i njezin operativni kontekst, a zatim izvana vratite odgovarajući statusni kod.
{
"error": {
"code": "inventory_unavailable",
"message": "Jedna ili više stavki više nije dostupno."
}
}
To potrošačima API-ja također daje ugovor na kojem mogu graditi. Promjena interne klase iznimke ne bi trebala prisiliti svakog klijenta da promijeni rukovanje pogreškama.
Neka baza podataka štiti istinu
Validacija aplikacije poboljšava korisničko iskustvo, ali nije zamjena za ograničenja baze podataka. Dva istodobna zahtjeva mogu oba proći provjeru „postoji li ova e-adresa?” prije nego što ijedan umetne redak. Jedinstveni indeks ispravno rješava tu utrku.
Koristite strane ključeve gdje je odnos stvaran, ograničenja NOT NULL za obavezne vrijednosti, ograničenja CHECK gdje su podržana i prikladna te pažljivo odabrane jedinstvene indekse za poslovne identifikatore. Promjene koje moraju uspjeti zajedno obuhvatite transakcijom, ali transakcije neka budu kratke. Držanje transakcije otvorenom tijekom pozivanja udaljene usluge produljuje vrijeme zaključavanja i otežava dijagnosticiranje sukoba.
Evolucija sheme zaslužuje jednaku pažnju kao i aplikacijski kod. Sigurna implementacija obično slijedi obrazac proširenja i sužavanja: najprije dodajte stupac koji dopušta NULL ili novu tablicu, implementirajte kod koji može raditi s oba oblika, po potrebi popunite postojeće podatke, prebacite čitanja i pisanja, a zatim u kasnijem izdanju uklonite staru strukturu. Time se izbjegava prekid rada instanci koje tijekom postupne implementacije još poslužuju promet.
Spremnici standardiziraju isporuku, a ne dizajn
Docker čini lokalna i implementirana okruženja dosljednijima, što je vrijedno. Sam po sebi ne čini uslugu nadziranom, sigurnom ni skalabilnom. Korisna slika spremnika ima jasnu naredbu za izvršavanje, konfiguraciju dostavljenu kroz okruženje ili mehanizam tajni te se za trajne podatke ne oslanja na zapisivo lokalno stanje.
Za PHP aplikacije razlikujte web-izvršavanje od dugotrajnih radnika. Radniku reda potrebni su strategija ponovnog pokretanja, koordinacija implementacije i nadzor memorije; nije samo još jedna kopija HTTP procesa. Osigurajte da se radnici ponovno pokrenu kada se promijeni aplikacijski kod i omogućite uredno gašenje kako posao u tijeku ne bi bio napušten na pola izvršavanja.
Provjere stanja trebale bi odgovarati na konkretna pitanja. Provjera živosti može utvrditi da proces radi. Provjera spremnosti može utvrditi da može prihvatiti promet. Izbjegavajte pretvaranje lagane krajnje točke za provjeru živosti u lanac poziva prema svakoj ovisnosti jer privremeni prekid rada baze podataka može uzrokovati nepotrebna ponovna pokretanja.
Optimizirajte nakon što možete objasniti opterećenje
Rad na performansama najučinkovitiji je kada počinje konkretnim pitanjem: koja je krajnja točka spora, pri kakvom obliku podataka i gdje se troši vrijeme? Izmjerite broj i trajanje upita, pregledajte planove izvršavanja skupih upita i potražite nepotrebnu serijalizaciju ili mrežne pozive prije nego što posegnete za predmemorijom.
Predmemoriranje je kompromis između brzine i svježine. Predmemorirajte podatke s eksplicitnim vlasnikom, strategijom ključeva, pravilom isteka i planom poništavanja. Ako tim ne može objasniti kada vrijednost postaje zastarjela i kako se ispravlja, predmemorija će vjerojatno kasnije stvoriti suptilan problem ispravnosti.
Arhitektura je navika očuvanja mogućnosti
Cilj nije savršen dijagram apstrakcije. Cilj je API koji može prihvatiti sljedeću promjenu bez pretvaranja svake krajnje točke u rizičnu izmjenu. Zadržite HTTP aspekte tankima, smjestite pravila ondje gdje se mogu ponovno upotrijebiti, učinite ponašanje pri kvaru eksplicitnim, koristite bazu podataka za očuvanje invarijanti i implementirajte promjene u kompatibilnim koracima.
Okviri ubrzavaju isporuku. Trajna arhitektura štiti brzinu isporuke nakon što laki dio završi. To je razlika između API-ja koji se samo pokrene i onoga koji nastavlja zasluživati povjerenje kako njegove odgovornosti rastu.