Arhitektura sustava: Zašto su jednostavan i održiv kôd najbolji izbor
Većina sustava ne propada zato što timu nedostaje domišljatosti. Propadaju zato što obične promjene postanu rizične: novo polje API-ja zahtijeva izmjene na pet mjesta, migracija baze podataka ima nejasno vlasništvo, Docker slika se u produkciji ponaša drukčije ili je „privremenu” apstrakciju sada nemoguće ukloniti.
Protuotrov je rijetko moderna arhitektura. To je dosadan kôd koji se može održavati: kôd s očitim granicama, poznatim alatima, predvidljivim načinima otkaza i troškom koji ostaje razumljiv nakon što se zaboravi izvorna implementacija.
Dosadno je dizajnerski izbor, a ne nedostatak ambicije
„Dosadno” ne znači nemarno, staromodno ili otporno na poboljšanja. Znači odabrati najjednostavniji dizajn koji zadovoljava trenutačnu operativnu potrebu i koji može sigurno promijeniti kompetentan programer koji nije bio prisutan kada je dizajn nastao.
Konvencionalna PHP aplikacija s jasnim tijekom zahtjeva, relacijskom bazom podataka, eksplicitnim migracijama, pozadinskim radnicima prema potrebi i malim brojem dobro definiranih integracija često je snažniji sustav od skupa servisa povezanih redovima čekanja, generiranim klijentima i prilagođenom infrastrukturom.
Složenost ima naviku stići prije svojih koristi. Svaki dodatni servis donosi pitanja implementacije, zapisivanja, konfiguracije, autentikacije, nadzora, verzioniranja i odgovora na incidente. Ti troškovi mogu biti opravdani, ali trebali bi se plaćati za stvaran problem, a ne zamišljenu budućnost.
Optimizirajte za sljedeću sigurnu promjenu
O arhitekturi se često govori kao o dijagramu. U praksi se njezina kvaliteta pokazuje kada netko mora napraviti promjenu tijekom napornog tjedna. Mogu li pronaći relevantan kôd? Mogu li ga testirati lokalno? Mogu li utvrditi koji će se podaci promijeniti? Mogu li ga implementirati bez koordiniranja desetak nepovezanih komponenti?
Sustav koji se može održavati čini svoj najčešći put eksplicitnim. Za tipičnu pozadinsku krajnju točku to može biti:
- kontroler koji validira i prevodi HTTP zahtjev;
- aplikacijski servis koji izvršava slučaj uporabe;
- repozitorij ili sloj upita koji posjeduje pojedinosti pohrane;
- pravila na razini domene izražena blizu podataka koje štite; i
- preslikavač odgovora koji API reprezentaciju održava promišljenom.
Te granice nisu zahtjev za ceremonijom. Maloj krajnjoj točki možda trebaju samo kontroler i servis. Poanta je izbjeći skrivanje poslovnih pravila u kôdu predloška, objektima zahtjeva, ORM povratnim pozivima ili raspršenim pomoćnim funkcijama. Kada pravilo ima ime i mjesto, može se testirati i mijenjati.
Učinite ovisnosti vidljivima
Skrivene ovisnosti stvaraju iznenađujuće ponašanje. Metoda koja tiho čita trenutačnog korisnika, otvara transakciju, šalje e-poruku i poništava predmemoriju može isprva biti praktična, ali o njoj je kasnije teško rasuđivati.
Dajte prednost eksplicitnim ulazima i eksplicitnim ishodima. Servis nazvan approveInvoice() trebao bi jasno pokazati zapisuje li podatke, emitira događaj ili može odbiti zahtjev. Iznimke su korisne za iznimne neuspjehe, ali očekivani poslovni ishodi često su jasniji kada se izravno prikažu u tijeku aplikacije.
final class ApproveInvoice
{
public function __construct(
private InvoiceRepository $invoices,
private TransactionManager $transactions,
) {}
public function handle(int $invoiceId, int $approverId): void
{
$this->transactions->run(function () use ($invoiceId, $approverId): void {
$invoice = $this->invoices->getForUpdate($invoiceId);
$invoice->approve($approverId);
$this->invoices->save($invoice);
});
}
}
Točna sučelja razlikovat će se, ali važan je dio vidljivo ponašanje. Čitatelj može vidjeti da ova operacija mijenja trajno stanje i zahtijeva transakcijsku obradu.
Neka baza podataka štiti istinu
Validacija aplikacije poboljšava povratne informacije korisnicima; ograničenja baze podataka štite sustav. Oboje je važno. Ako adresa e-pošte mora biti jedinstvena ili podređeni red ne može postojati bez nadređenog, kodirajte to pravilo u shemi kao i u aplikacijskom kôdu.
To je posebno važno pri istodobnom radu. Dva zahtjeva mogu oba proći provjeru na razini aplikacije „postoji li ovo?” prije nego što bilo koji od njih upiše podatke. Jedinstveni indeks konačni je autoritet. Aplikacija bi zatim trebala čisto obraditi nastali sukob umjesto pretpostavljati da će vrijeme uvijek biti naklonjeno.
Promjene baze podataka zaslužuju istu disciplinu kao promjene kôda. Koristite verzionirane migracije, namjerno provodite destruktivne operacije i razmotrite redoslijed implementacije. Dodavanje nullable stupca obično je lakše uvesti nego odmah zahtijevati novu vrijednost koja nije null od svake stare instance aplikacije. Popunjavanje postojećih podataka i promjene ograničenja mogu biti zasebni koraci kada to čini vraćanje i provjeru sigurnijima.
Neka API-ji budu namjerno dosadni
API je ugovor, a ne samo ruta koja vraća JSON. Stabilno imenovanje, predvidljivi oblici pogrešaka, pravila paginacije i jasno ponašanje autentikacije smanjuju trenje za svakog pozivatelja.
Nemojte izravno izlagati tablice baze podataka u API odgovorima samo zato što ORM to olakšava. Oblik odgovora trebao bi odražavati ono što potrošači trebaju, a ne svaki stupac koji slučajno postoji. To također daje pozadinskom sustavu prostor za reorganizaciju interne pohrane bez prisiljavanja klijenata na promjene.
Kod promjena je aditivna evolucija obično susretljivija od zamjene. Uvedite novo neobavezno polje, podržite ga kod svih pozivatelja, a zatim povucite staro polje kroz najavljen i mjeren proces. Verzije mogu biti korisne, ali ne uklanjaju potrebu za pažljivim upravljanjem kompatibilnošću.
Koristite Docker za smanjenje razlika, a ne dodavanje slojeva
Spremnici su vrijedni kada lokalni razvoj, testiranje i implementaciju čine ponovljivijima. Postaju štetni kada je Docker postavka tajanstvenija od aplikacije koju pokreće.
Dobra izgradnja spremnika je čitljiva: deterministički instalira ovisnosti, kopira samo ono što je potrebno, pruža konfiguraciju kroz okruženje ili upravljani mehanizam tajni te pokreće istu naredbu aplikacije koju očekujete u produkciji. Kada je praktično, odvojite pogodnosti namijenjene samo razvoju od produkcijske slike.
Nemojte uspješnu izgradnju spremnika zamijeniti za zdravu implementaciju. Aplikacija se i dalje mora pokrenuti sa svojom stvarnom konfiguracijom, dosegnuti svoje ovisnosti i izložiti smislene provjere zdravlja. Provjera zdravlja trebala bi pokazati da proces može služiti svojoj namijenjenoj ulozi, a ne samo da je port otvoren.
Performanse počinju mjerenjem i jednostavnošću
Rad na performansama jedno je od najlakših mjesta za stvaranje trajne složenosti kao odgovor na privremeni simptom. Prije uvođenja predmemorije, reda čekanja, replike ili nove pohrane podataka, utvrdite stvarno usko grlo. Je li spori put neindeksirani upit, slučajni N+1 upit, ponovljeni udaljeni pozivi, prevelika veličina korisnog tereta ili zasićen radnik?
Počnite s najmanje invazivnim rješenjem. Poboljšajte upit i provjerite njegov plan. Namjerno dohvatite povezane podatke. Dodajte paginaciju. Postavite vremenska ograničenja za odlazne pozive. Ograničeno ponašanje ponovnog pokušaja primijenite oko neuspjeha koji su doista prolazni i učinite upise koji se mogu ponoviti sigurnima putem idempotentnosti ili trajne strategije deduplikacije.
Predmemorije su korisne, ali dodaju još jednu istinu koju treba sinkronizirati. Prije nego što predmemoriranje smatrate zadanom optimizacijom, tretirajte poništavanje, istek, zastarjela čitanja i prekide rada predmemorije kao uobičajena dizajnerska pitanja.
Neka operativno ponašanje bude dio arhitekture
Sustav se može održavati samo ako se može razumjeti dok radi. Strukturirani zapisi trebali bi sadržavati dovoljno konteksta za povezivanje zahtjeva, posla ili neuspjeha s relevantnom operacijom bez otkrivanja osjetljivih podataka. Metrike bi trebale odgovoriti na operativna pitanja: ne uspijevaju li zahtjevi, gomilaju li se poslovi i mijenja li se latencija? Upozorenja bi trebala biti vezana uz uvjete koji zahtijevaju akciju.
Jednako je važno dokumentirati sitnice koje bi inače ostale u sjećanju: kako pokrenuti migracije, kako sigurno ponovno izvršiti neuspjeli posao, što promjena vraća mijenja i koja ovisnost posjeduje konfiguracijsku vrijednost. Dokumentacija ne mora biti duga; mora odgovarati stvarnosti.
Trajna prednost
Kôd koji se može održavati nije manje sposoban od pametnog kôda. To je kôd koji domišljatost čuva za mjesta na kojima ona zaslužuje svoj trošak. Timovima omogućuje da energiju troše na poslovno pravilo, problem korisnika ili rizik pouzdanosti koji proizvod doista razlikuje.
Najbolje arhitektonsko pitanje često nije „Što bi bilo najimpresivnije?” Nego: „Što će sljedeću nužnu promjenu učiniti sigurnom, jasnom i reverzibilnom?” Odaberite odgovor koji sustav održava razumljivim. Za godinu dana taj prividni nedostatak drame možda će biti najvrjednija inženjerska odluka koju ste donijeli.