Razvoj

Beyond the Framework: Building Maintainable APIs That Last

Izvan okvira: Izgradnja održivih API-ja koji traju

Framework može učiniti da API djeluje dovršeno mnogo prije nego što je zaista dugotrajan. Rute su čiste, kontroleri su tanki, migracije se izvršavaju, a nekoliko krajnjih točaka vraća JSON. Zatim stižu zahtjevi: mobilnom klijentu treba preimenovati polje, tijek plaćanja mora se moći sigurno ponovno pokušati, upit za izvještavanje postaje spor ili pozadinski radnik treba ista poslovna pravila kao HTTP sloj.

Framework nikada nije bio problem. Problem je dopustiti mu da postane mjesto na kojem se donosi svaka odluka.

API-ji koje je moguće održavati traju zato što njihova važna pravila ostaju razumljiva kada se promijene prijenos, pohrana, implementacija i struktura tima. Dobar framework ubrzava taj rad. Ne bi trebao određivati granice sustava.

Smjestite poslovne odluke iza stabilnih granica

HTTP kontroler trebao bi prevesti zahtjev u radnju aplikacije, a zatim rezultat prevesti u odgovor. Ne bi trebao odlučivati o ispunjavanju uvjeta za popust, sastavljati upite baze podataka za svaku granu ili ugrađeno koordinirati tijek rada u više koraka.

U PHP-u to često znači premještanje smislenog ponašanja u aplikacijske servise ili klase usmjerene na domenu. Nazivi su manje važni od razdvajanja: kod koji izražava poslovna pravila ne bi smio izravno ovisiti o objektima zahtjeva ili JSON odgovorima.

final class PlaceOrder
{
    public function __construct(
        private OrderRepository $orders,
        private InventoryGateway $inventory
    ) {
    }

    public function handle(PlaceOrderCommand $command): Order
    {
        $this->inventory->reserve($command->items);

        $order = Order::place(
            customerId: $command->customerId,
            items: $command->items
        );

        $this->orders->save($order);

        return $order;
    }
}

Kontroler može pozvati ovu klasu, radnik u redu može je pozvati, a uvoz iz naredbenog retka može je pozvati. Framework ostaje koristan na rubovima, dok osnovno ponašanje postaje lakše testirati i ponovno koristiti.

Nemojte ovo načelo pretvoriti u ceremoniju. Jednostavna krajnja točka za čitanje može opravdano upitivati putem sloja baze podataka frameworka i izravno vratiti resurs. Uvedite granicu ondje gdje štiti složenost, a ne ondje gdje samo dodaje datoteke.

Oblikujte odgovore kao ugovore, a ne snimke stanja baze podataka

Izravno izlaganje modela praktično je dok se model ne promijeni. Stupci baze podataka optimizirani su za pohranu i interne operacije; API odgovori obećanja su dana klijentima. To su različite brige.

Namjerno definirajte oblike odgovora. Uključite polja zato što ih korisnici trebaju, a ne zato što slučajno postoje u tablici. To također pojednostavljuje sigurnosni pregled: osjetljiva ili interna polja ne mogu procuriti samo zato što je netko dodao stupac.

Kompatibilnost zaslužuje jednaku pažnju. Preimenovanje polja odgovora nije mala refaktorizacija ako ga klijenti već čitaju. Dodajte novo polje, dokumentirajte prijelaz i uklonite staro polje tek nakon što korisnici migriraju. Verzije mogu biti korisne, ali nisu zamjena za pažljivu evoluciju unutar verzije.

Učinite odgovore o neuspjehu predvidljivima

Klijentima je potreban pouzdan način razlikovanja neispravnog unosa, nedostajućih resursa, neuspjeha autentikacije i neočekivanih pogrešaka poslužitelja. Zadržite dosljedan format i izbjegavajte izlaganje tragova stoga ili internih poruka iznimki.

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

Točna shema manje je važna od dosljednosti. Predvidljiv ugovor o pogreškama smanjuje grananje na strani klijenta i čini razgovore s podrškom mnogo jasnijima.

Koristite bazu podataka kao partnera

Mnogi problemi s performansama API-ja ne rješavaju se u PHP-u. Počinju nejasnim obrascima pristupa: popisivanjem zapisa prema statusu i datumu, učitavanjem povezanih podataka za odgovor ili pretraživanjem povijesti klijenta. Namjerno oblikujte te upite, a zatim pregledajte plan izvršavanja baze podataka kada su performanse važne.

Indeksi trebaju služiti stvarnim upitima. Indeks na svakom stupcu povećava trošak pisanja i otežava održavanje; nepostojeći indeks na često filtriranom ili spojenom putu može običnu krajnju točku pretvoriti u usko grlo. Paginacija također zahtijeva promišljen dizajn. Paginacija s pomakom jednostavna je i često prihvatljiva za male administrativne popise, dok je paginacija temeljena na kursoru obično stabilnija za velike skupove podataka koji se mijenjaju.

Transakcije zaslužuju jednaku pažnju. Ako operacija mora ili stvoriti narudžbu i rezervirati zalihe ili ne učiniti ni jedno, te promjene trebaju izričitu strategiju dosljednosti. Ne može svaka ovisnost sudjelovati u istoj transakciji baze podataka, osobito vanjske usluge. U tim slučajevima dizajnirajte za ponovno pokušavanje i usklađivanje umjesto da pretpostavljate kako jedan zahtjev može učiniti distribuirani rad atomarnim.

Pretpostavite da će se zahtjevi ponavljati

Mreže mogu otkazati nakon što je poslužitelj već obradio zahtjev. Korisnici dvaput kliknu. Radnici za poslove mogu ponovno pokušati nakon vremenskog ograničenja. Za krajnje točke koje mijenjaju stanje, idempotentnost je često razlika između rješivog incidenta i dvostrukih naplata, narudžbi ili obavijesti.

Ključ idempotentnosti poslužitelju daje način da prepozna ponovljenu namjeru. Pohranite ključ s rezultatom operacije, prikladno ga ograničite na pozivatelja i vratite izvorni rezultat za podudarajuće ponavljanje. Budite precizni u pogledu ponašanja kod sukoba: ponovna upotreba istog ključa s materijalno drukčijim zahtjevom ne bi smjela tiho stvoriti drugu operaciju.

Ponovni pokušaji također trebaju biti selektivni. Ponovno pokušavanje privremenog neuspjeha veze može imati smisla; ponovno pokušavanje pogreške validacije je uzaludno. Ako radnik šalje e-poštu nakon potvrđivanja promjene u bazi podataka, upotrijebite trajan mehanizam za bilježenje rada prije njegovog slanja. Time se smanjuje razmak u kojem rušenje procesa može izgubiti važnu nuspojavu.

Učinite implementaciju namjerno dosadnom

Docker može učiniti lokalni razvoj i implementaciju ponovljivijima, ali slika spremnika nije operativna strategija. Konfiguracija bi trebala dolaziti iz okruženja ili upravljanog mehanizma za tajne, nikada iz vrijednosti ugrađenih u kontrolu izvornog koda ili sloj slike.

Neka slika za izvršavanje bude usmjerena: instalirajte samo ono što aplikacija treba, izričito pokrenite namjeravani proces i osigurajte da se spremnik može pokrenuti iz čistog okruženja. Migracije baze podataka trebale bi biti namjeran korak implementacije, a ne slučajna nuspojava pokretanja svakog web procesa. Migracija koja zaključava veliku tablicu ili ponovno zapisuje postojeće podatke treba isti pregled kao i aplikacijski kod.

Zapisivanje, provjere zdravlja i vremenska ograničenja također pripadaju dizajnu. Strukturirani zapisi s identifikatorima zahtjeva ili korelacije čine neuspjehe sljedivima kroz HTTP obrađivače i radnike. Vremenska ograničenja sprječavaju širenje iscrpljenih resursa zbog prekida u nizvodnim sustavima kroz cijelu uslugu. Provjere zdravlja trebale bi pokazivati može li proces obavljati svoju namjeravanu ulogu, umjesto da samo dokazuju da je port otvoren.

Optimizirajte za sljedeću promjenu

Najvrjednija arhitektura API-ja nije ona s najviše obrazaca. To je ona u kojoj razvojni inženjer može brzo odgovoriti na praktična pitanja: gdje se ovo pravilo provodi, na koji ugovor odgovora utječe, kako se testira i što se događa ako se operacija pokrene dvaput?

  • Zadržite kod frameworka blizu mehanizama isporuke kao što su HTTP, redovi i naredbe.
  • Učinite poslovna pravila pozivljivima bez sastavljanja web zahtjeva.
  • API korisne podatke i pogreške tretirajte kao izričite ugovore s klijentima.
  • Izmjerite ponašanje baze podataka prije nego što posegnete za optimizacijom na razini aplikacije.
  • Oblikujte pisanja, poslove i integracije za ponovne pokušaje i djelomične neuspjehe.

Frameworki će se razvijati, ovisnosti će se zamjenjivati, a platforme za implementaciju mijenjat će se. Jasne granice, promišljeni ugovori i iskreno rukovanje neuspjesima preživljavaju te promjene. Tako API postaje više od zbirke krajnjih točaka: postaje sustav koji može nastaviti zasluživati povjerenje kako proizvod raste.

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.