Više od CRUD-a: Arhitektura API-ja koja raste s vašim ambicijama
Većina API-ja započinje kao smislen skup CRUD krajnjih točaka. Stvorite kupca, dohvatite narudžbu, ažurirajte profil, izbrišite zapis. To je često upravo ispravno polazište. Problem počinje kada API neprimjetno postane granica između proizvoda, timova, integracija i poslovnih procesa, dok njegov dizajn i dalje pretpostavlja da je samo tanki omotač baze podataka.
Skaliranje API-ja nije samo pitanje obrade više zahtjeva. Riječ je o omogućavanju rasta proizvoda bez pretvaranja svake nove značajke u rizičnu migraciju, promjenu odgovora koja narušava kompatibilnost ili zamršenu logiku kontrolera. Arhitektura bi trebala važne odluke učiniti izričitima: što API obećava, gdje se nalaze poslovna pravila, kako se podaci sigurno mijenjaju i kako se komuniciraju neuspjesi.
Dizajnirajte prema sposobnostima, a ne tablicama
Shema baze podataka opisuje kako se podaci pohranjuju. API bi trebao opisivati što klijenti mogu učiniti. To su povezana pitanja, ali nisu isti ugovor.
Krajnja točka poput POST /orders može biti korisno polazište, ali zrelom sustavu često trebaju jasnije poslovne radnje: slanje narudžbe, njezino otkazivanje, odobravanje povrata novca, rezerviranje zaliha ili izdavanje računa. Te operacije imaju pravila, dozvole, nuspojave i prijelaze stanja koji se ne uklapaju lako u „ažuriraj ovaj redak”.
Na primjer, generička krajnja točka za ažuriranje može slučajno dopustiti nemoguća stanja:
{
"status": "shipped",
"payment_status": "unpaid"
}
Umjesto toga, modelirajte radnju i validirajte je na jednom mjestu:
POST /orders/ord_123/ship
Zahtjev može tražiti informacije potrebne za slanje narudžbe, dok aplikacijska usluga provjerava plaćanje, zalihe, trenutačno stanje narudžbe i autorizaciju. API klijentima postaje lakši za razumijevanje jer izražava namjeru umjesto da izlaže svako interno polje.
Učinite ugovor trajnim
Klijenti ne ovise samo o putanjama krajnjih točaka. Ovise o nazivima polja, ponašanju validacije, pravilima paginacije, oblicima pogrešaka, zahtjevima za autentikaciju i značenju statusnih kodova. Sve to tretirajte kao ugovor proizvoda.
Trajni ugovori daju prednost aditivnim promjenama. Dodavanje neobaveznog polja odgovora obično je sigurnije od njegova preimenovanja. Uvođenje nove krajnje točke obično je sigurnije od promjene značenja postojećeg polja. Ako je promjena koja narušava kompatibilnost neizbježna, namjerno uvedite verziju i potrošačima pružite put migracije umjesto da neprimjetno mijenjate ponašanje u produkciji.
Odgovori na pogreške zaslužuju jednaku pažnju kao i uspješni odgovori. Klijent bi trebao moći razlikovati nevaljan unos od nedostajućeg resursa, neuspjeha autorizacije i privremenog problema usluge. Dosljedan oblik također sprječava gomilanje posebnih slučajeva u kodu sučelja i integracija.
{
"error": {
"code": "validation_failed",
"message": "Zahtjev sadrži nevaljana polja.",
"details": {
"email": ["Polje e-pošte mora sadržavati valjanu adresu."]
}
}
}
Ne izlažite pozivateljima iznimke baze podataka, tragove stoga ni poruke specifične za radni okvir. Sigurno zabilježite koristan interni kontekst, ali vratite stabilan javni rječnik pogrešaka.
Neka HTTP bude tanak, a poslovna logika kohezivna
U PHP aplikaciji kontroleri su prikladna ulazna točka, a ne dom poslovnih pravila sustava. Kontroler bi trebao autentificirati zahtjev, validirati njegov unos na razini prijenosa, pozvati aplikacijsku uslugu i pretvoriti rezultat u HTTP odgovor. Kada kontroler počne koordinirati transakcije, provjere zaliha, e-poruke i revizijske zapise, postaje ga teško testirati, a još teže ponovno upotrijebiti.
Praktično razdvajanje često izgleda ovako:
- Kontroleri prevode HTTP zahtjeve i odgovore.
- Aplikacijske usluge koordiniraju slučaj upotrebe, poput naručivanja.
- Domenska pravila provode invarijante i valjane prijelaze stanja.
- Repozitoriji ili slojevi pristupa podacima dohvaćaju i trajno pohranjuju podatke bez posvudašnjeg otkrivanja pojedinosti pohrane.
- Infrastrukturni adapteri upravljaju redovima čekanja, e-poštom, pružateljima plaćanja, predmemorijama i vanjskim API-jima.
Ovo nije argument za ceremonijalne slojeve u maloj aplikaciji. To je podsjetnik da složenost smjestite tamo gdje se može imenovati, testirati i mijenjati. Počnite jednostavno, a zatim izdvojite granicu kada se neka odgovornost ponovno koristi, sklona je neuspjesima ili će se vjerojatno razvijati neovisno.
Zaštitite upise transakcijama i idempotentnošću
Performanse čitanja privlače pozornost, ali netočni upisi uzrokuju dugotrajniju štetu. Svaki tijek rada koji mijenja više zapisa ili pokreće naknadni rad treba jasnu strategiju dosljednosti.
Koristite transakciju baze podataka za promjene koje moraju zajedno uspjeti ili ne uspjeti. Primjerice, bilježenje narudžbe, rezerviranje zaliha i stvaranje zapisa o plaćanju mogu pripadati jednoj transakciji ako dijele istu bazu podataka i moraju ostati dosljedni. Transakcije neka budu kratke: nemojte držati zaključavanja baze podataka dok pozivate pristupnik za plaćanje ili šaljete e-poruku.
Vanjske nuspojave uvode drugi problem. Klijent može ponoviti pokušaj nakon isteka vremena čak i kada je poslužitelj dovršio izvorni zahtjev. Za važne krajnje točke za stvaranje ili radnje podržite ključ idempotentnosti. Pohranite ključ s ishodom zahtjeva i vratite izvorni rezultat kada se isti ključ ponovno prikaže. Time se mrežni ponovni pokušaj od mogućeg dvostrukog terećenja ili dvostruke narudžbe pretvara u predvidivu operaciju.
Za asinkroni rad zabilježite namjeravani događaj uz transakciju, a zatim ga zasebno obradite. Time se izbjegava krhki obrazac potvrđivanja promjene u bazi podataka uz nadu da će odmah sljedeći mrežni poziv uspjeti. Radnik može sigurno ponavljati pokušaje, uz vidljivost neuspjeha i definiran put za operacije koje zahtijevaju ručnu pozornost.
Planirajte upite prije nego što postanu incidenti
Performanse se rijetko rješavaju dodavanjem predmemorije nejasnom upitu. Počnite s obrascem pristupa. Koji popis klijent treba? Kako se filtrira i sortira? Koja se polja zapravo prikazuju? Koliko se povezanih zapisa učitava po stavci?
Paginacija bi trebala biti izričita i ograničena. Paginacija s pomakom jednostavna je za mnoge administrativne zaslone. Za velike skupove podataka koji se često mijenjaju, paginacija temeljena na kursoru može omogućiti stabilnije kretanje kada je redoslijed sortiranja dobro definiran. Koji god pristup odabrali, dokumentirajte redoslijed i najveću veličinu stranice.
Pazite na slučajne N+1 upite u kodu vođenom ORM-om: jedan upit za popis, a zatim još jedan upit za relaciju svake stavke. Unaprijed učitajte relacije koje zahtijeva krajnja točka, odaberite samo potrebne stupce i dodajte indekse koji podržavaju stvarne kombinacije filtriranja i sortiranja. Indeksi nisu ukrasi; svaki ima troškove upisivanja i pohrane, stoga trebaju odgovarati na uočenu potrebu upita.
Namjerno učinite implementaciju dosadnom
Docker može učiniti lokalna i implementacijska okruženja ponovljivijima, ali slika spremnika ne uklanja operativnu odgovornost. Konfiguraciju držite izvan slike, pokrećite migracije baze podataka kao kontrolirani korak implementacije i osigurajte da se aplikacija može pokrenuti samo s konfiguracijom koja joj je uistinu potrebna.
Provjere stanja trebale bi razlikovati „proces je pokrenut” od „aplikacija može posluživati promet”. Zapisi bi trebali biti dovoljno strukturirani da povežu zahtjev, pogrešku i pozadinski zadatak bez bilježenja tajni ili osjetljivih sadržaja. Metrike i praćenje najvrjedniji su kada odgovaraju na praktična pitanja: koja je krajnja točka spora, koja ovisnost ne uspijeva i povećavaju li se ponovni pokušaji.
Skalirajte odluke prije infrastrukture
Najjača arhitektura API-ja nije ona s najviše obrazaca. To je ona koja čuva jasne ugovore, štiti važne promjene stanja i sljedećem razvojnom programeru daje očito mjesto za dodavanje sljedeće sposobnosti.
CRUD je koristan jezik za trajnu pohranu. Ambicioznim sustavima potreban je bogatiji jezik za ponašanje. Kada API govori poslovnim radnjama, zadržava svoju složenost iza namjernih granica i neuspjeh tretira kao uobičajeno stanje dizajna, može prerasti iz korisnog sučelja u pouzdanu infrastrukturu.