Razvoj

System Architecture: Designing APIs for Evolving Needs

Arhitektura sustava: Dizajniranje API-ja za potrebe koje se razvijaju

Većina API-ja ne propada zato što je njihova prva verzija bila loše dizajnirana. Propadaju zato što prva verzija tiho postane ugovor za potrebe koje nitko nije predvidio: pojavi se mobilni klijent, partneru su potrebni webhookovi, izvještavanje zahtijeva povijesne podatke ili naizgled bezopasno polje postane ključno za poslovanje.

Dobra arhitektura sustava ne pokušava predvidjeti svaku buduću značajku. Ona stvara granice koje promjene čine promišljenima, vidljivima i prihvatljivima. Za PHP backend timove to obično znači tretirati API kao nešto više od ruta i kontrolera: on je dugoročan dogovor između klijenata, aplikacijske logike, podataka i operacija.

Počnite sa stabilnim poslovnim konceptima

Krajnje točke trebale bi odražavati koncepte koji će vjerojatno ostati smisleni čak i kada se implementacija promijeni. „Narudžbe”, „kupci” i „pretplate” općenito su čvršći temelji od krajnjih točaka oblikovanih prema današnjem rasporedu zaslona ili spajanjima tablica u bazi podataka.

Česta početna pogreška jest izravno izlaganje struktura za pohranu podataka. Ako tablica orders sadrži interne oznake, strane ključeve i operativne vremenske oznake, vraćanje tog retka kao JSON-a povezuje klijente s odlukama o pohrani. Kasnije uređivanje sheme tada postaje događaj koji narušava API kompatibilnost.

Umjesto toga, namjerno definirajte modele odgovora. Kontroler može od aplikacijske usluge zatražiti prikaz narudžbe, dok transformator odlučuje što javni ugovor sadržava.

final class OrderResource
{
    public static function fromOrder(Order $order): array
    {
        return [
            'id' => (string) $order->id(),
            'status' => $order->status()->value,
            'total' => [
                'amount' => $order->total()->amount(),
                'currency' => $order->total()->currency(),
            ],
        ];
    }
}

To nije formalnost radi formalnosti. Bazi podataka, domenskom modelu i javnom API-ju daje prostor da se razvijaju različitim brzinama.

Održite slojeve dosljednima

Mnoge backend baze koda započinju s tankim kontrolerima, a zatim brzo nakupe validaciju, autorizaciju, izračune, SQL upite, obavijesti i logiku ponovnih pokušaja u istoj radnji. Krajnja točka i dalje radi, ali svaki novi klijent ili pozadinski posao mora ili duplicirati ponašanje ili interno pozivati HTTP.

Praktična podjela je jednostavna:

  • Kontroleri prevode HTTP zahtjeve u aplikacijske pozive i oblikuju HTTP odgovore.
  • Aplikacijske usluge koordiniraju slučajeve uporabe kao što su slanje narudžbe ili otkazivanje pretplate.
  • დომenski kod provodi poslovna pravila koja moraju vrijediti bez obzira na to započinje li rad putem HTTP-a, reda čekanja ili posla iz naredbenog retka.
  • Infrastrukturni prilagodnici upravljaju bazama podataka, predmemorijama, pružateljima e-pošte i vanjskim API-jima.

Cilj nije prisiliti svaku klasu u obrazac iz udžbenika. Cilj je spriječiti da detalji transporta i dobavljača postanu mjesto na kojem žive poslovne odluke. Ako potrošač reda čekanja mora izvesti istu operaciju kao API krajnja točka, oboje bi trebali moći pozvati istu aplikacijsku uslugu.

Dizajnirajte za proširenja, ne samo za verzije

Verzioniranje je korisno kada se ugovor mora nekompatibilno promijeniti, ali nije zamjena za pažljivu evoluciju. Novo neobavezno polje odgovora obično je sigurnije od promjene značenja ili vrste postojećeg polja. Dodavanje nove krajnje točke obično je sigurnije od redefiniranja stare krajnje točke oko novog radnog tijeka.

Prije stvaranja /v2, zapitajte se može li se potreba riješiti dodatnom promjenom. Klijenti često zaostaju za implementacijama na poslužitelju, a održavanje više potpunih verzija API-ja umnaža posao dokumentiranja, testiranja, sigurnosne provjere i podrške.

Kada je promjena koja narušava kompatibilnost neizbježna, migraciju učinite izričitom. Objavite novi ugovor, sačuvajte stari tijekom definiranog razdoblja zastarijevanja, pratite stvarnu upotrebu i vratite jasne pogreške kada se zastarjelo ponašanje naposljetku ukloni. Putanja verzije poput /api/v1/orders lako je razumljiva, ali važna arhitektonska odluka jest politika životnog ciklusa koja stoji iza nje.

Koristite izričitu semantiku

Dvosmislenost stvara više problema s kompatibilnošću nego nedostatak značajki. Odlučite što izostavljanje znači u zahtjevima za ažuriranje. Zadržava li odsutno polje vrijednost nepromijenjenom, briše li je ili primjenjuje zadanu vrijednost? Neka identifikatori, vremenske oznake, vrijednosti valuta, straničenje i strukture pogrešaka budu dosljedni u cijelom API-ju.

Primjerice, odgovor o pogrešci trebao bi klijentu omogućiti da razlikuje neuspjelu validaciju od neuspjele autorizacije ili prolaznog problema poslužitelja. Predvidiva omotnica čini integracije manje krhkima:

{
  "error": {
    "code": "validation_failed",
    "message": "The request contains invalid fields.",
    "fields": {
      "email": ["A valid email address is required."]
    }
  }
}

Zaštitite bazu podataka od pritiska API-ja

API može biti uredan na razini kontrolera, a ipak postati spor ili nepouzdan zato što su njegova čitanja i pisanja loše oblikovana. Krajnje točke za popise zaslužuju posebnu pozornost. Neograničeni skupovi rezultata, upiti za odnose po retku i široka spajanja mogu korisnu krajnju točku pretvoriti u produkcijski incident kako podaci rastu.

Koristite straničenje s dokumentiranom najvećom veličinom stranice. Učinkovito učitajte poznate odnose umjesto da ih upitujete jednu stavku odjednom. Odaberite samo stupce potrebne za odgovor. Za velike zbirke ili zbirke koje se često mijenjaju, straničenje temeljeno na pokazivaču može ponuditi stabilnije kretanje kroz rezultate od brojeva stranica, ali zahtijeva stabilan, dokumentiran redoslijed.

Putanje pisanja zahtijevaju jednaku pažnju. Zahtjev koji stvara plaćanje, šalje e-poštu i poziva partnersku uslugu ne može sigurno pretpostaviti da će svaka nizvodna radnja uspjeti zajedno. Primarno poslovno stanje pohranite transakcijski, a zatim vanjske nuspojave predajte putem trajnog asinkronog mehanizma primjerenog sustavu. Temeljno je načelo da su potvrda transakcije baze podataka i udaljeni HTTP poziv odvojene domene kvara.

Idempotentnost je još jedna ključna granica. Ako klijent ponovi pokušaj nakon isteka vremena, poslužitelj ne bi smio slučajno stvoriti dvije narudžbe. Za operacije kod kojih su duplikati štetni, prihvatite ključ idempotentnosti, povežite ga s ishodom zahtjeva i vratite izvorni rezultat pri ponavljanju istog zahtjeva.

Učinite asinkroni rad vidljivim i oporavljivim

Redovi čekanja poboljšavaju odzivnost, ali ne čine složenost nestalom. Svaki posao treba jasnu politiku ponovnih pokušaja, najveći broj pokušaja i put za neuspjehe koji zahtijevaju ljudsku pozornost. Ponovni pokušaj privremene mrežne pogreške može biti smislen; neograničeno ponavljanje pokušaja s nevaljanim korisnim teretom nije.

Poslove bi trebalo biti sigurno pokrenuti više puta jer radnici mogu zakazati nakon što obave dio posla. Zabilježite dovoljno stanja za otkrivanje dovršenog rada, koristite jedinstvene vanjske reference gdje su dostupne i evidentirajte identifikatore koji povezuju API zahtjev s njegovom pozadinskom obradom.

Operativna vidljivost dio je arhitekture. Mjerite latenciju zahtjeva, stope pogrešaka, dubinu reda čekanja, neuspjele poslove, opterećenje veza s bazom podataka i spore upite. Zapisi bi trebali sadržavati identifikator zahtjeva ili korelacije, bez otkrivanja lozinki, tokena ili osjetljivih osobnih podataka. Kada se ovisnost pogorša, ti signali pretvaraju nagađanje u dijagnozu.

Uvodite promjene kao slijed

Promjene sheme i implementacije aplikacije moraju podnositi kratko razdoblje tijekom kojega stari i novi kod postoje istodobno. Dajte prednost migracijama proširi-pa-smanji: najprije dodajte stupac koji dopušta NULL ili novu tablicu, implementirajte kod koji može raditi s oba oblika, po potrebi popunite podatke, a zatim uklonite staru strukturu tek nakon što se više ne koristi.

Docker pomaže da se izvođenje može ponoviti, ali slika spremnika sama po sebi nije strategija implementacije. Konfiguracija bi trebala dolaziti iz okruženja, tajne bi se trebale unositi kroz odgovarajući proces upravljanja tajnama, a migracije bi se trebale pokretati kao kontrolirani korak implementacije, a ne kao slučajna nuspojava pokretanja svakog web spremnika.

Arhitektura za API-je koji se razvijaju u konačnici je vježba očuvanja mogućnosti. Neka ugovori budu namjerni, izolirajte poslovna pravila, poštujte rast podataka i načine kvara te učinite svaku promjenu vidljivom. Najbolji API nije onaj koji je izgledao savršeno na dan lansiranja. To je onaj koji može prihvatiti sljedeću razumnu promjenu bez guranja svojih korisnika — ili održavatelja — u krizu.

Portret autora bloga

Mihajlo

Ja sam Mihajlo — programer vođen znatiželjom, disciplinom i stalnom željom da stvorim nešto smisleno. Dijelim uvide, tutorijale i besplatne usluge kako bih pomogao drugima da pojednostave svoj rad i rastu u svijetu softvera i umjetne inteligencije koji se neprestano razvija.