Razvoj

Beyond APIs: Architecting for Seamless System Evolution

Izvan API-ja: Arhitektura za besprijekornu evoluciju sustava

API može omogućiti komunikaciju između dvaju sustava. Ne može ih sam po sebi učiniti sposobnima za siguran razvoj.

Ta je razlika važnija kako PHP aplikacija prerasta jedan kôd i jednu bazu podataka. Uredan REST krajnji pristup može skrivati krhku stvarnost: zajedničke tablice, nedokumentirane pretpostavke, čvrsto povezane Docker implementacije i korisnike koji polje tumače na suptilno različite načine. Prva integracija uspijeva. Peta promjena postaje skupa.

Neometana evolucija sustava disciplina je koja promjenu čini uobičajenom. API-ji su dio te discipline, ali stvarna arhitektura nalazi se u granicama, vlasništvu nad podacima, praksama implementacije i načinu postupanja s kvarovima.

Dizajnirajte ugovore, a ne samo krajnje točke

Definicija krajnje točke samo je djelomičan ugovor. Potpuni ugovor uključuje značenje polja, zadane vrijednosti, jamstva redoslijeda, ponašanje pri pogreškama, autentikaciju, paginaciju, idempotentnost i ono što se događa kada je ovisnost spora ili nedostupna.

Razmotrite uslugu narudžbi koja u početku uz svaku narudžbu vraća e-poštu kupca:

{
  "id": "ord_123",
  "status": "paid",
  "customer_email": "[email protected]"
}

Uklanjanje customer_email može izgledati kao bezazleno poboljšanje privatnosti ako drugo polje sada identificira kupca. Za radnik za izvoz, alat za izvještavanje ili mobilni klijent to može biti prekidna promjena u produkciji. Polje nije bilo samo podatak; bilo je ovisnost.

Kad je moguće, preferirajte aditivne promjene. Uvedite zamjensko polje, dokumentirajte njegovo značenje, dajte korisnicima vremena za migraciju i izmjerite traži li se ili koristi staro polje prije uklanjanja. Verzije mogu pomoći, ali nisu zamjena za pažljivo upravljanje ugovorima. API s verzijama koji dijeli nestabilnu bazu podataka i dalje je nestabilan.

Učinite vlasništvo vidljivim

Najvažnije pitanje u distribuiranom sustavu često nije „koja usluga izlaže ovu krajnju točku?” Nego „koja komponenta posjeduje ovu činjenicu?”

Ako usluga naplate posjeduje stanje računa, druge usluge ne bi trebale izravno ažurirati tablice naplate. Izravan pristup u početku djeluje učinkovito, osobito u PHP monolitu koji se postupno razlaže. Također stvara nevidljive ovisnosti: migracija sheme sada zahtijeva koordinaciju s kôdom koji se nikada nije trebao oslanjati na tu tablicu.

Vlasništvo ne zahtijeva trenutačne mikroservise. Modularni monolit može pružiti izvrsne granice kada moduli komuniciraju putem eksplicitnih aplikacijskih sučelja, a migracije se tretiraju kao unutarnji detalji implementacije. Praktični je cilj spriječiti jedno područje sustava da zadire u drugo samo zato što je veza s bazom podataka dostupna.

  • Dodijelite svakoj domeni jasnog vlasnika za pisanja i poslovna pravila.
  • Namjerno izlažite modele za čitanje umjesto da svakom korisniku dopuštate upite nad operativnim tablicama.
  • Bilježite važne promjene stanja kao domenske događaje kada druge komponente doista trebaju reagirati.
  • Držite terete događaja usredotočenima na stabilne poslovne činjenice, a ne na sirove retke baze podataka.

Neka se baza podataka razvija u fazama

Promjene baze podataka zaslužuju isto razmišljanje o kompatibilnosti kao javni API-ji. Implementacija koja dodaje stupac koji ne dopušta null vrijednosti, odmah ga zahtijeva u aplikacijskom kôdu, a zatim implementira sve korisnike pretpostavlja savršeno vrijeme. Stvarne implementacije uključuju postupno zamjenjivanje instanci, odgođene radnike, neuspjela izdanja i ručni oporavak.

Sigurniji je obrazac proširi, migriraj, smanji. Najprije dodajte kompatibilnu promjenu sheme. Zatim implementirajte kôd koji može čitati stare i nove oblike. Popunite postojeće zapise u kontroliranim skupinama. Kada su svi pisci i čitači prešli, uklonite zastarjelu putanju u kasnijem izdanju.

Na primjer, podjela jednog stupca name na first_name i last_name ne bi trebala početi uklanjanjem name. Dodajte nove stupce koji dopuštaju null vrijednosti, ažurirajte pisce da popunjavaju oba prikaza, popunite povijesne podatke tamo gdje je podjela pouzdana, migrirajte čitače i tek tada povucite staro polje. Neke povijesne vrijednosti mogu biti dvosmislene; plan migracije to bi trebao priznati umjesto da potajno izmišlja sigurnost.

Koristite transakcije za lokalnu istinu, a ne globalnu nadu

Transakcija baze podataka idealna je za očuvanje konzistentnosti unutar jedne ograničene operacije. Ne može sigurno koordinirati svaku vanjsku radnju. Ako transakcija stvori narudžbu, a zatim pozove pružatelja plaćanja, istek vremena stvara dvosmislenost: pružatelj je možda obradio zahtjev iako aplikacija nije primila odgovor.

Koristite ključeve idempotentnosti za operacije koje se mogu ponoviti i trajno zabilježite namjeru objave važnog naknadnog rada. Transakcijski outbox čest je pristup: zapišite poslovnu promjenu i zapis izlaznog događaja u istoj lokalnoj transakciji, a zatim dopustite radniku da isporuči događaj. Radnik mora tolerirati dvostruku isporuku jer su pouzdani sustavi izgrađeni oko ponovnih pokušaja, a ne oko pretpostavke da svaka poruka stiže točno jednom.

Gradite za djelomični neuspjeh

Svaki mrežni poziv može kasno ne uspjeti, dvaput ne uspjeti ili uspjeti bez da odgovor stigne do pozivatelja. Putanje kôda trebale bi tu stvarnost učiniti eksplicitnom.

$response = $client->request('POST', '/charges', [
    'headers' => ['Idempotency-Key' => $paymentAttemptId],
    'json' => $payload,
]);

if ($response->getStatusCode() >= 500) {
    throw new RetryablePaymentException();
}

Ovo je tek polazište. Trenutačno ponovno pokušavanje svake greške može pojačati prekid rada. Postavite ograničena vremena isteka, razlikujte pogreške koje se mogu ponoviti od pogrešaka validacije, koristite odgodu i pošaljite iscrpljeni rad na putanju neuspjeha koja se može pregledati. Najvažnije, osigurajte da je sama operacija sigurna za ponavljanje. Pravilo ponovnog pokušaja bez idempotentnosti pravilo je za dvostruku naplatu koje čeka da se dogodi.

Vremena isteka treba odabrati na svakoj granici: dolazni zahtjevi, upiti bazi podataka, konzumiranje reda i HTTP klijenti. Nepostojanje vremena isteka nije strpljenje; to je nekontrolirana obveza resursa.

Neka implementacija bude dosadna

Docker pomaže učiniti okruženja izvođenja ponovljivima, ali spremnici ne uklanjaju operativni dizajn. Konfiguracija bi trebala dolaziti iz okruženja ili mehanizma za upravljanje tajnama prikladnog platformi za implementaciju, a ne iz slojeva slike ili predanih konfiguracijskih datoteka. Slike bi trebalo izgraditi jednom i promovirati između okruženja umjesto da se ponovno izgrađuju s različitim skrivenim pretpostavkama.

Provjere zdravlja trebale bi odgovoriti na korisna pitanja. Provjera živosti može pokazati da PHP proces radi. Provjera spremnosti trebala bi pokazati da instanca može poslužiti promet koji će uskoro primiti, bez pretvaranja svakog prolaznog problema s ovisnošću u oluju ponovnih pokretanja.

Implementacije također trebaju prozore kompatibilnosti. Korisnici mogu obrađivati poruke iz reda nakon što je proizvođač ažuriran. Radnici mogu kratko pokretati stariji kôd tijekom postupnog izdanja. Dizajnirajte sheme i poruke tako da stare i nove verzije mogu koegzistirati dovoljno dugo da se uvođenje dovrši.

Optimizirajte nakon što možete vidjeti sustav

Rad na performansama najučinkovitiji je kada počinje s izmjerenim uskim grlom. Prije dodavanja predmemoriranja provjerite broj upita, planove upita, veličinu tereta, zaostatak reda i latenciju vanjskih poziva. U PHP aplikacijama poznati problemi poput N+1 upita, nepotrebne serijalizacije i velikih zbirki u memoriji često su važniji od egzotičnih infrastrukturnih promjena.

Predmemoriranje je također ugovor. Odlučite što unos čini zastarjelim, tko ga poništava i što korisnici vide kada nije dostupan. Predmemorija koja tiho vraća zastarjele podatke o autorizaciji nije samo značajka performansi; ona je rizik za ispravnost.

Evolucija je arhitektonska značajka

Najjači sustavi nisu oni koji se nikada ne mijenjaju. To su oni koji se mogu mijenjati bez prisiljavanja svakog tima, usluge, baze podataka i implementacije da se kreću istim korakom.

Počnite tako da sučelja tretirate kao obećanja, vlasništvo nad podacima kao dizajnersku odluku, a ponovne pokušaje kao uobičajeno ponašanje. Dodajte kompatibilnost prije uklanjanja. Promatrajte prije optimiziranja. Učinite sljedeću promjenu lakšom od prethodne.

To je rad izvan API-ja: izgradnja softvera koji ostaje razumljiv i pouzdan dok se sve oko njega razvija.

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.