Iza monolita: projektiranje otpornosti sustava koji se razvija
Monoliti se često krive za probleme koje nisu stvorili. Jedna aplikacija koja se može implementirati može se brzo izgraditi, lako razumjeti i biti sasvim prikladna za mali tim. Problemi počinju kada njezine granice prestanu odgovarati promjenjivim potrebama sustava: spor upit za izvještavanje usporava naplatu, jedna implementacija ugrožava nepovezane značajke ili se ispad usluge treće strane pretvara u incident na cijeloj stranici.
Otpornost nije isto što i podjela svega na servise. Ona je sposobnost da se nastave isporučivati najvažniji ishodi dok se mijenjaju ovisnosti, obrasci prometa i zahtjevi. Dobra arhitektura čini te promjene jeftinijima, sigurnijima i lakšima za razumijevanje.
Počnite s točkama pritiska, a ne dijagramom mikroservisa
Prije izdvajanja bilo čega utvrdite gdje je monolit doista pod pritiskom. Tražite različite profile skaliranja, potrebe za neovisnim izdanjima, suprotstavljene zahtjeve za pouzdanošću i nejasnoće oko vlasništva. Tijek plaćanja i zadatak izvoza PDF-a možda danas žive u istoj PHP aplikaciji, ali ne zaslužuju nužno isto ponašanje tijekom izvođenja.
Korisno pitanje glasi: što mora nastaviti raditi ako je ova komponenta spora, nedostupna ili se implementira? Ako korisnici mogu naručivati bez trenutačnog primanja e-poruke s potvrdom, isporuka e-pošte snažan je kandidat za asinkronu izolaciju. Ako raspodjela zaliha mora biti točna prije naplate, njezino prerano odvajanje može uvesti više rizika nego što uklanja.
- Izdvajajte prema poslovnoj sposobnosti: prednost dajte ograničenom području poput obavijesti, indeksiranja pretraživanja ili obrade medija, a ne tehničkom sloju poput „cijelog koda baze podataka”.
- Izdvajajte prema operativnoj potrebi: komponente s različitim prometom, načinima otkaza ili ritmom implementacije najviše imaju koristi od neovisnosti.
- Očuvajte jasan izvor istine: svaki važan dio podataka treba imati nedvosmislenog vlasnika, čak i dok su sustavi u prijelazu.
Najprije izgradite granice unutar monolita
Najsigurniji put izvan monolita obično počinje unutar njega. Učinite module eksplicitnima, smanjite skriveno sprezanje i definirajte sučelja prije uvođenja mrežne granice. To donosi vrijednost čak i ako se nijedan servis nikada ne izdvoji.
U PHP-u to često znači premještanje logike kontrolera opterećene okvirom prema aplikacijskim servisima s uskim ovisnostima. Radnja naplate trebala bi orkestrirati slučaj uporabe, a ne izravno znati kako se prikazuje e-pošta, rezerviraju zalihe i pohranjuju analitički događaji. Ovisnosti se tada mogu zamijeniti, odgoditi ili premjestiti iza API-ja bez ponovnog pisanja poslovnog tijeka.
final class PlaceOrder
{
public function __construct(
private OrderRepository $orders,
private InventoryGateway $inventory,
private EventPublisher $events,
) {}
public function handle(PlaceOrderCommand $command): Order
{
$order = Order::create($command->customerId, $command->items);
$this->inventory->reserve($order->items());
$this->orders->save($order);
$this->events->publish(new OrderPlaced($order->id()));
return $order;
}
}
Sučelja su važnija od sintakse. InventoryGateway u početku može pozivati lokalni kod. Kasnije može pozivati servis, koristiti pravilo ponovnih pokušaja ili izložiti kontrolirani pričuvni mehanizam. Pozivatelj ne bi trebao morati učiti te operativne pojedinosti.
Koristite asinkroni rad za ograničavanje otkaza
Sinkroni lanci su krhki. Ako HTTP zahtjev prije odgovora mora zapisati narudžbu, naplatiti karticu, generirati dokument, obavijestiti skladište, poslati e-poštu i ažurirati CRM, dostupnost krajnje točke postaje umnožak dostupnosti svake ovisnosti. To također uzrokuje lošu latenciju i otežanu dijagnozu incidenata.
Premjestite nekritični naknadni rad u trajni red. Riječ trajni je važna: objavljivanje poruke tek nakon potvrde transakcije baze podataka sprječava radnike da vide podatke koji su poništeni. Za promjene koje moraju pouzdano stvoriti i stanje baze podataka i događaj, tablica za odlazne događaje praktičan je obrazac. Pohranite događaj s poslovnim ažuriranjem, a zatim neka ga radnik objavi i označi kao obrađen.
Radnici moraju očekivati duplikate i djelomične otkaze. Red može ponovno isporučiti poruku nakon isteka vremena, a proces može otkazati nakon nuspojave, ali prije bilježenja uspjeha. Dizajnirajte potrošače tako da budu idempotentni: koristite stabilan identifikator događaja, gdje je prikladno osigurajte jedinstvenu obradu i učinite ponovljeno izvršavanje bezopasnim.
Ponovni pokušaji trebaju granice
Ponovno pokušavanje svakog neuspjeha nije otpornost. Ponovno pokušavanje nevaljanog zahtjeva troši kapacitet; ponovno pokušavanje privremeno nedostupne ovisnosti može pomoći. Koristite ograničen broj pokušaja, sve dulja odgađanja i put za neisporučive poruke koje zahtijevaju istragu. Zabilježite dovoljno konteksta za praćenje neuspjeha, ali izbjegavajte stavljati vjerodajnice ili nepotrebne osobne podatke u zapise.
Za udaljeni API namjerno postavite vremenska ograničenja povezivanja i odgovora. Izostanak vremenskog ograničenja može zauzeti PHP radnike dok cijela aplikacija ne postane spora. Ako ovisnost očito otkazuje, prekidač strujnog kruga ili privremena degradacija značajke mogu zaštititi primarni tijek rada. „Narudžba je zaprimljena; potvrda će uslijediti” često je bolji ishod od nedostupnosti naplate zato što je pružatelj obavijesti nedostupan.
Pažljivo razvijajte vlasništvo nad podacima
Odvajanje baza podataka jedan je od najtežih dijelova distribuirane arhitekture. Zajednička baza podataka čini izdvajanje lakim, ali održava servise spojenima kroz sheme, migracije, zaključavanja i nedokumentirane upite. Često je prijelazni korak, a ne krajnje stanje.
Kada sposobnost postane neovisna, dodijelite joj vlasništvo nad njezinim zapisima i podatke koje namjerava da drugi koriste izložite putem ugovora. Druge komponente mogu zadržati lokalne modele za čitanje kada trebaju brz pristup. To uvodi eventualnu konzistentnost, pa to moraju priznati korisničko iskustvo i poslovna pravila. Indeks pretraživanja može kasniti za ažuriranjem proizvoda; knjiga plaćanja ne može to nepromišljeno učiniti.
Izbjegavajte distribuirane transakcije kao zadani izlaz u nuždi. Dajte prednost eksplicitnim stanjima i kompenzacijskim radnjama. Na primjer, narudžba može postati pending_payment, zatim paid kada plaćanje uspije, ili payment_failed kada ne uspije. Ta stanja čine oporavak vidljivim i operativno upravljivim.
Neka implementacija bude dosadna i uočljiva
Spremnici pomažu standardizirati izvršavanje, ali Docker sam po sebi ne stvara otpornost. Korisna slika ima predvidivo okruženje za izvođenje, konfiguraciju isporučenu kroz okruženje ili mehanizam za tajne te nema skrivenu ovisnost o datotekama stvorenima prethodnom implementacijom. Pažljivo pokrećite migracije baze podataka: proširite sheme prije nego što kod ovisi o njima, implementirajte kompatibilan kod, a zatim u kasnijem izdanju uklonite zastarjela polja.
Promatrivost bi od početka trebala prelaziti granice. Prenesite identifikator zahtjeva ili korelacije u zapise, poruke reda i odlazne HTTP pozive. Pratite signale koji pomažu odgovoriti na praktična pitanja: je li krajnja točka spora, gomilaju li se zadaci, otkazuje li ovisnost i koja je poslovna operacija pogođena? Metrike, strukturirani zapisi i tragovi najvrjedniji su kada povezuju tehničke simptome s određenim putem kroz sustav.
- Definirajte zdravstvene provjere koje razlikuju pokrenut proces od komponente sposobne pružati koristan rad.
- Postavite ograničenja resursa kako jedan potrošač reda ili zadatak izvještavanja ne bi iscrpio poslužitelj.
- Testirajte vraćanje na prethodnu verziju i degradirano ponašanje, a ne samo implementaciju u idealnom scenariju.
- Dokumentirajte vlasništvo, ugovore i postupke oporavka uz kod.
Otpornost je arhitektonska navika
Cilj nije eliminirati monolit ni maksimizirati broj jedinica koje se mogu implementirati. Cilj je stvoriti granice koje odražavaju stvarne odgovornosti, ograničiti otkaze i omogućiti sustavu da se mijenja bez pretvaranja svake promjene u koordinirani događaj.
Dobro strukturiran monolit često je prvi izraz te discipline. Servisi, redovi, zasebne baze podataka i spremnici postaju korisni kada rješavaju dokazani problem. Arhitekturu oblikujte za sljedeću značajnu točku pritiska, održavajte kritični put kratkim i oporavak učinite osmišljenim ponašanjem, a ne improviziranim. Tako sustav ostaje pouzdan dok se sve oko njega razvija.