Razvoj

Pragmatic PHP: Build APIs That Scale Without the Boilerplate

Pragmatični PHP: Gradite API-je koji se skaliraju bez suvišnog predloška

Skaliranje API-ja rijetko je ograničeno samim PHP-om. Češće je ograničeno aplikacijom koja je svaki zahtjev pretvorila u dugi lanac skrivenog rada: učitavanje previše podataka, izvršavanje upita unutar petlji, povezivanje HTTP pitanja s poslovnim pravilima i tretiranje implementacije kao zasebnog problema za kasnije.

Pragmatični PHP znači oduprijeti se tom skretanju. Ne znači izbjegavati strukturu. Znači odabrati strukturu koja se sama isplati: jasne granice, predvidiv pristup podacima, uočljivi kvarovi i runtime koji se jednako ponaša na prijenosnom računalu i u produkciji.

Započnite s malim, eksplicitnim putem zahtjeva

API krajnju točku trebalo bi biti lako pratiti od rute do odgovora. Korisna početna osnova jest razdvojiti transport, aplikacijsku logiku i infrastrukturu bez stvaranja klase za svaki redak koda.

Kontroler treba validirati zahtjev i oblikovati odgovor. Servis ili akcija trebaju izraziti slučaj upotrebe. Repozitorij ili objekt upita trebaju upravljati detaljima perzistencije. Time se sprječava prodor objekata specifičnih za okvir u jezgru aplikacije.

final class CreateOrderAction
{
    public function __construct(
        private OrderRepository $orders,
        private TransactionManager $transactions
    ) {
    }

    public function execute(CreateOrder $command): Order
    {
        return $this->transactions->run(function () use ($command) {
            $order = Order::create(
                customerId: $command->customerId,
                items: $command->items
            );

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

            return $order;
        });
    }
}

Ovo nije arhitektura radi arhitekture. Akcija jasno ističe jedinicu rada. Testovima daje usredotočenu metu, a vlasništvo nad transakcijom čini eksplicitnim. Kontroler može prevesti neuspjehe validacije u odgovor klijentu bez odlučivanja o načinu perzistiranja narudžbi.

Učinite rad s bazom podataka vidljivim

Većina problema s performansama API-ja zapravo su problemi baze podataka prerušeni u probleme aplikacijskog sloja. Brza krajnja točka nije ona koja koristi domišljatu sintaksu; to je ona koja od baze podataka traži prave podatke u ograničenom broju operacija.

Pripazite na klasični obrazac N+1. Dohvaćanje stranice narudžbi, a zatim učitavanje kupca ili stavki za svaku narudžbu, može izgledati bezopasno u lokalnom razvoju. Pod opterećenjem postaje množitelj upita. Upotrijebite željno učitavanje, spajanja ili namjenski upit za čitanje kada odgovor zahtijeva povezane podatke.

Paginacija zaslužuje jednaku disciplinu. Paginacija s pomakom je poznata, ali veliki pomaci mogu postati skupi i nedosljedni kako se redovi mijenjaju. Kada klijenti prirodno napreduju kroz stabilan redoslijed, paginacija kursorom često je prikladnija. Upotrijebite deterministički ključ sortiranja, uključite kriterij za razrješenje izjednačenja poput ID-ja i kodirajte samo stanje potrebno za sljedeći zahtjev.

  • Indeksirajte stupce koji se koriste za filtriranje, spajanje i sortiranje na temelju stvarnih obrazaca upita.
  • Odaberite samo polja potrebna krajnjoj točki; izbjegavajte učitavanje cijelog zapisa prema zadanim postavkama.
  • Postavite razumne granice veličine stranice kako se jedan zahtjev ne bi slučajno pretvorio u skupni izvoz.
  • Pregledajte planove upita kada upit promijeni oblik ili postane usko grlo u produkciji.

Transakcije trebaju biti kratke. Ne držite transakciju baze podataka otvorenom dok pozivate drugi servis, šaljete e-poštu ili generirate izvješće. Najprije trajno spremite promjenu stanja, a zatim proslijedite naknadni rad putem pouzdanog mehanizma primjerenog jamstvima isporuke sustava.

Dizajnirajte API-je za promjene, a ne samo za prvog klijenta

HTTP API-ji brzo postaju ugovori. Dosljednost je vrjednija od domišljatog naziva krajnje točke. Odaberite konvencije za imenovanje resursa, terete pogrešaka, paginaciju, vremenske oznake i identifikatore, a zatim ih primjenjujte svugdje.

Pogreške validacije trebaju pomoći klijentima da isprave zahtjev. Odgovori o sukobu trebaju komunicirati stvarni sukob stanja, a ne prikrivati neočekivanu iznimku. Pogreške poslužitelja trebaju se zapisivati s dovoljno konteksta zahtjeva za istragu, dok klijent prima sigurnu, stabilnu poruku.

Idempotentnost je važna kad god klijenti mogu ponoviti pisanja. Mreže otkazuju, balansatori opterećenja prekoračuju vremensko ograničenje, a korisnici dvaput šalju obrasce. Za operaciju poput stvaranja plaćanja ili dodjele računa, ključ idempotentnosti omogućuje poslužitelju da prepozna ponovljeni zahtjev i vrati rezultat izvorne operacije umjesto da je izvrši dvaput. Spremite ključ zajedno s odgovarajućim identitetom zahtjeva i rezultatom te definirajte koliko dugo ostaje valjan.

Verzioniranje treba biti promišljeno, ali ne bi smjelo biti prvi odgovor na svaku promjenu. Dodajte neobavezna polja kada ih klijenti mogu sigurno zanemariti. Uvedite novu krajnju točku kada se semantika doista razlikuje. Novu verziju zadržite za promjene koje postojeći klijenti ne mogu sigurno protumačiti.

Upotrijebite Docker za uklanjanje iznenađenja u okruženju

Spremnici su najkorisniji kada smanjuju razlike između razvoja, testiranja i implementacije. Slika PHP aplikacije treba sadržavati potrebna PHP proširenja, aplikacijski kod i predvidivu naredbu za pokretanje. Konfiguracija koja se razlikuje po okruženju treba dolaziti iz varijabli okruženja ili upravljane konfiguracije, a ne iz izmijenjenih slika.

FROM php:8.3-fpm-alpine

WORKDIR /app

COPY composer.json composer.lock ./
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
    && rm composer-setup.php \
    && composer install --no-dev --prefer-dist --no-interaction

COPY . .

CMD ["php-fpm"]

Točna strategija za sliku razlikovat će se ovisno o platformi za implementaciju, ali načelo ostaje isto: izgradite jednom, konfigurirajte tijekom izvođenja i zadržite sliku reproducibilnom. U praksi produkcijske slike također trebaju neadministratorskog korisnika za izvođenje, odgovarajuće dozvole za datoteke i procesni model koji odgovara web-poslužitelju ili platformi ispred PHP-FPM-a.

Pažljivo predmemorirajte i mjerite prije slavljenja

Predmemoriranje može dobar API učiniti jeftinijim i bržim. Također može zbunjujući API učiniti težim za otklanjanje pogrešaka. Počnite s podacima čiji je izračun skup, koji se često čitaju i koje je sigurno poslužiti i ako su neznatno zastarjeli. Prije dodavanja predmemorije definirajte ključ predmemorije, istek, put poništavanja i ponašanje pri neuspjehu.

Predmemorija ne bi trebala postati jedino mjesto na kojem zahtjev može uspjeti. Ako nije dostupna, aplikacija bi se trebala sigurno vratiti na izvor istine ili jasno ne uspjeti kada je svježina ključna. Izbjegavajte predmemoriranje pogrešaka osim ako je takvo ponašanje namjerno i kratkotrajno.

Mjerite latenciju krajnjih točaka, stope pogrešaka, vrijeme rada baze podataka, dubinu reda gdje je relevantno i zasićenje resursa. Zapisnici trebaju uključivati identifikator zahtjeva ili korelacije. Metrike vam govore da problem postoji; tragovi i strukturirani zapisnici pomažu objasniti koja ga je ovisnost ili put koda uzrokovao.

Održavajte bazu koda jednostavnom za mijenjanje

Održivost je značajka performansi za timove. Male metode, izravni nazivi i testovi oko važnog ponašanja čine sigurnijim poboljšavanje API-ja mjesecima nakon izvorne implementacije. Dajte prednost testovima koji provjeravaju stvarne granice na kojima su neuspjesi vjerojatni: validaciju, autorizaciju, perzistenciju, serijalizaciju i integraciju s infrastrukturnim prilagodnicima.

Ne slijedite apstrakciju prije nego što ponavljanje otkrije stabilan koncept. Izravan objekt upita obično je bolji od generičkog repozitorija koji ne može izraziti upit koji vam zaista treba. Usredotočena akcija obično je bolja od razgranatog servisa koji upravlja nepovezanim tijekovima rada.

Dosadni put često je onaj koji se može skalirati

API-ji se skaliraju kada je njihovo ponašanje razumljivo pod pritiskom. Zadržite putanje zahtjeva eksplicitnima, ograničite rad baze podataka, učinite ponovne pokušaje sigurnima, predvidivo pakirajte ovisnosti tijekom izvođenja i promatrajte sustav prije njegova ugađanja. PHP je u potpunosti sposoban podržati zahtjevne sustave kada je okolni inženjerski rad discipliniran.

Cilj nije baza koda koja na dijagramu izgleda sofisticirano. To je sustav koji se može mijenjati, otklanjati pogreške i koristiti s pouzdanjem. To je vrsta skalabilnosti koja traje.

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.