Razvoj

Stop Architecting for Features, Start Building for Flow

Prestanite projektirati za funkcionalnosti, počnite graditi za tok rada

Mnogi pozadinski sustavi postanu složeni mnogo prije nego što postanu veliki. Uobičajeni uzrok nisu promet, veličina tima ni egzotičan problem s bazom podataka. To je dizajnerska navika: tretiranje svake zatražene značajke kao novog arhitektonskog objekta.

Stigne značajka pa se pojavi nova usluga. Novi krajnji API endpoint dobije vlastiti sloj orkestracije. Mala varijacija u tijeku rada postane još jedan skup tablica, zastavica, događaja i iznimaka. Pojedinačno, svaka odluka izgleda razumno. Zajedno stvaraju sustav optimiziran za objašnjavanje svojih dijelova, umjesto za pouzdano provođenje rada od zahtjeva do ishoda.

Snažna arhitektura manje se odnosi na broj granica koje možete povući, a više na to koliko se glatko važni tokovi kreću kroz njih.

Značajke su imenice; tok je glagol

Dizajn usmjeren na značajke prirodno se usredotočuje na imenice: pretplate, račune, izvoze, obavijesti, profile, dozvole. Te su imenice važne, ali korisnici doživljavaju glagole. Registriraju se, plaćaju, uvoze, pretražuju, ponovno pokušavaju, preuzimaju i oporavljaju se.

Arhitektura usmjerena na tok započinje pitanjem što se mora dogoditi od početka do kraja. Razmotrite korisnika koji zatraži izvoz računa:

  • API autentificira i autorizira zahtjev.
  • Sustav bilježi da je izvoz zatražen.
  • Radnik prikuplja podatke bez držanja HTTP veze otvorenom.
  • Generirana datoteka sigurno se pohranjuje.
  • Korisnik dobiva trajan način da je preuzme.
  • Neuspjesi se mogu ponoviti bez stvaranja zbunjujućih duplikata.

To je stvarna jedinica dizajna. Endpoint za izvoz, zadatak u redu čekanja, prilagodnik za pohranu, zapis baze podataka i obavijest pomoćni su detalji. Ako se ti detalji dizajniraju neovisno, sretan put može funkcionirati, dok ponovni pokušaji, djelomični neuspjesi i istrage podrške postaju bolni.

Mapirajte put prije odabira obrasca

Prije uvođenja usluge, događaja, apstrakcije ili novog modela baze podataka, opišite tok običnim jezikom. Uključite stanja, vlasnika svake tranzicije i što se događa kada je ovisnost spora ili nedostupna.

Korisna provjera jest može li razvojni inženjer koji nije upoznat s kodom odgovoriti na ova pitanja:

  • Što pokreće ovu operaciju?
  • Gdje je zabilježeno njezino trenutačno stanje?
  • Koji se korak može sigurno ponoviti?
  • Kako izgleda uspjeh?
  • Kako neuspjeh izgleda korisniku, a kako operateru?
  • Kako se pronalazi i nastavlja nedovršen posao?

Ako odgovori zahtijevaju pretraživanje kroz kontrolere, slušatelje, cron zadatke i nekoliko repozitorija, sustav možda ima granice bez koherentnog toka.

To ne znači da svaka operacija treba formalni mehanizam za upravljanje tijekom rada. Većina PHP aplikacija ima koristi od nečeg jednostavnijeg: eksplicitnog polja stanja, male aplikacijske usluge, granice transakcije i zadatka u redu čekanja za rad koji se ne bi trebao odvijati tijekom zahtjeva.

Učinite stanje vidljivim

Skriveno stanje jedan je od najskupljih oblika složenosti. Zapis označen kao completed dok njegova naknadna obavijest tiho nije uspjela ostavlja i korisnika i tim za podršku sa sustavom dvosmislenog stanja.

Dajte prednost smislenim, vidljivim stanjima kao što su requested, processing, ready i failed. Ona bi trebala opisivati poslovnu operaciju, a ne samo posljednju metodu koja se izvršila. Prijelaz stanja također bi trebao imati jasno pravilo: tko ga smije izvršiti, pod kojim uvjetima i je li ga sigurno ponovno pokušati.

DB::transaction(function () use ($accountId) {
    $export = AccountExport::create([
        'account_id' => $accountId,
        'status' => 'requested',
    ]);

    GenerateAccountExport::dispatch($export->id);
});

Važna ideja nije sintaksa okvira. To je ugovor: zahtjev se bilježi prije početka asinkronog rada. Ako slanje zahtijeva jača jamstva isporuke od predaje redu čekanja uz najbolji napor, upotrijebite trajni zapis u stilu outboxa i objavite ga zasebno. Nemojte pretpostaviti da su potvrda transakcije baze podataka i operacija vanjskog reda čekanja jedna atomska radnja, osim ako vaša infrastruktura izričito pruža to jamstvo.

Dizajnirajte API-je oko dovršetka, a ne oblika kontrolera

API endpoint obećanje je o toku. Za brze, determinističke operacije prikladan je sinkroni odgovor. Za spor ili promjenjiv rad, pretvaranje da svaki zahtjev može završiti unutar jednog HTTP odgovora stvara vremenska ograničenja, pritisak na memoriju i krhko ponašanje pri ponovnim pokušajima.

Za dugotrajnu operaciju vratite prihvaćeni odgovor s resursom koji se može naknadno provjeriti. Klijent može anketirati endpoint za status ili primiti obavijest putem kanala koji proizvod već podržava. Točan transport manje je važan od stabilnog životnog ciklusa.

POST /account-exports
Authorization: Bearer <token>

HTTP/1.1 202 Accepted
Location: /account-exports/exp_123

Idempotentnost pripada ovom razgovoru. Mrežni klijenti ponavljaju pokušaje. Balansatori opterećenja ponavljaju pokušaje. Ljudi kliknu dvaput. Ako je stvaranje iste operacije dvaput štetno, prihvatite ključ idempotentnosti i povežite ga s ishodom zahtjeva. Ponovni pokušaj trebao bi vratiti izvorni rezultat, a ne stvoriti drugi izvoz, naplatu ili niz e-poruka.

Održavajte granice transakcija poštenima

Transakcije baze podataka izvrsne su za zaštitu povezanih lokalnih promjena. Nisu rješenje opće namjene za koordinaciju udaljenih API-ja, redova čekanja, pružatelja e-pošte ili pohrane objekata.

Čest put neuspjeha izgleda ovako: kod sprema zapis o plaćanju, poziva vanjsku uslugu, zatim potvrđuje transakciju. Ako udaljeni poziv uspije, ali potvrda transakcije baze podataka ne uspije, udaljeni i lokalni svijet se ne slažu. Obrtanje redoslijeda samo mijenja koja je nedosljednost moguća.

Dizajn usmjeren na tok priznaje tu stvarnost. Lokalno zabilježite namjeru, izvedite udaljeni rad s tokenom idempotentnosti gdje je dostupan, pohranite rezultat i omogućite usklađivanje. Za osjetljive operacije izgradite jasan put popravka umjesto oslanjanja na jedno neprekinuto izvršavanje.

Ovakav način razmišljanja poboljšava i dizajn baze podataka. Tablice bi trebale čuvati informacije potrebne za objašnjenje trenutačnog stanja i oporavak od neuspjeha. Jedan preopterećeni stupac status može u početku biti dovoljan, ali nemojte odbaciti vremenske oznake, razloge neuspjeha, pokušaje ili vanjske referentne ID-jeve kada su ključni za upravljanje tokom.

Upotrijebite Docker kako bi tok bio ponovljiv

Spremnici su najvrjedniji kada smanjuju neizvjesnost okruženja. Tok PHP aplikacije često ovisi o više od PHP-a: web-poslužitelju, bazi podataka, predmemoriji, radniku reda čekanja, raspoređivaču, a ponekad i lokalnoj pohrani objekata ili alatu za presretanje pošte.

Cilj nije reproducirati produkciju uz maksimalnu ceremoniju. Cilj je omogućiti razvojnom inženjeru da pokrene ovisnosti potrebne za izvršavanje važnog puta i razumije koji proces posjeduje koju odgovornost.

Učinite radnika i raspoređivač eksplicitnima. Ako izvoz u redu čekanja uspije samo zato što razvojni inženjer slučajno pokrene naredbu u drugom terminalu, ta je ovisnost dio arhitekture, bez obzira na to je li dokumentirana ili nije.

services:
  app:
    build: .
  worker:
    build: .
    command: php artisan queue:work
  scheduler:
    build: .
    command: php artisan schedule:work

Nazivi i naredbe razlikovat će se ovisno o okviru, ali načelo ostaje: modelirajte procese koji pomiču tok naprijed. Zatim pružite provjere zdravlja, zapise i upute na razini operativnog priručnika primjerene operativnim potrebama sustava.

Optimizirajte put koji je važan

Rad na performansama također je lakši kada se promatra kao tok. Nemojte početi s „trebamo predmemoriranje”. Počnite sa sporim ili skupim putem: koji upit, korak serijalizacije, udaljeni poziv, zaključavanje ili zaostatak u redu čekanja produljuje korisniku vidljiv ishod?

Mjerite na granicama. Pratite trajanje zahtjeva, kašnjenje reda čekanja, trajanje zadatka, ponašanje upita baze podataka i stope pogrešaka ondje gdje su ti signali dostupni. Dodajte indekse zato što ih stvarni obrazac pristupa zahtijeva, a ne zato što se tablica čini važnom. Predmemorirajte stabilne, često čitane podatke s jasnim pravilima invalidacije, a ne cijele odgovore čija ispravnost ovisi o skrivenom korisničkom kontekstu.

Razmišljanje o toku sprječava lokalne optimizacije koje samo premještaju trošak drugdje. Brži endpoint koji stvara neograničen zaostatak u redu čekanja nije brži na način koji je korisnicima važan.

Neka granice služe putovanju

Moduli, usluge, repozitoriji, događaji i API-ji vrijedni su alati. Postaju štetni kada postoje uglavnom kako bi zadovoljili dijagram. Granica opravdava svoj trošak kada pojašnjava vlasništvo, izolira aspekt koji se mijenja ili čini kritični tok sigurnijim za razvoj.

Gradite oko rada koji vaš sustav mora pouzdano dovršiti. Učinite njegovo stanje eksplicitnim. Očekujte ponovne pokušaje i djelomične neuspjehe. Održavajte asinkrone korake vidljivima. Zatim odaberite najmanju arhitekturu koja to putovanje čini jasnim.

Značajke će nastaviti pristizati. Ako tok ostane razumljiv, svaka će imati smisleno mjesto kojem može pripadati.

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.