Ukroćivanje složenosti mikroservisa: Pragmatični vodič kroz arhitekturu
Mikrousluge se često uvode kao lijek za veliku aplikaciju koja se sporo razvija. U praksi mijenjaju jednu vrstu složenosti drugom. Modularni monolit može biti teško mijenjati jer su mu granice nejasne; sustav mikrousluga može biti teško mijenjati jer je svaka granica sada mrežni poziv, jedinica za implementaciju i potencijalna točka kvara.
Cilj nije zauvijek izbjegavati mikrousluge. Cilj je usvojiti ih za probleme koje doista rješavaju: domene koje se neovisno razvijaju, različite potrebe skaliranja, jasno vlasništvo ili smisleno izdvajanje. Dobra arhitektura mikrousluga manje se svodi na crtanje mnogo okvira, a više na držanje troška tih okvira pod kontrolom.
Počnite s granicama, a ne tehnologijama
Granica usluge trebala bi odražavati poslovnu sposobnost s vlastitim pravilima i rječnikom. „Narudžbe”, „naplata” i „identitet” mogu biti razumne granice jer predstavljaju različite odgovornosti. „Čitanje baze podataka”, „slanje e-pošte” i „validacija” obično sami po sebi nisu dovoljni; to su pitanja implementacije koja mogu pripadati široj domeni.
Za PHP pozadinski sustav to znači oduprijeti se porivu da se svaki kontroler ili Eloquent model razdvoji u zasebnu uslugu. Usluga bi trebala posjedovati koherentan tijek rada i podatke potrebne za njegovo izvršavanje. Ako se dvije komponente moraju implementirati zajedno, upitivati zajedno i mijenjati zajedno, možda su i dalje jedna usluga.
Korisno je zapitati se: može li ovaj tim opisati odgovornost usluge u jednoj rečenici bez spominjanja druge usluge? Ako je odgovor „obrađuje dio narudžbi koji poziva inventar i naplatu”, granica je vjerojatno još uvijek isprepletena.
Jasno odredite vlasništvo
Svaka usluga treba imati jasnog vlasnika, čak i kada više timova doprinosi platformi. Vlasništvo obuhvaća više od pisanja koda. Ono uključuje odluke o API-ju, operativno stanje, migracije baze podataka, sigurnosne zakrpe, dokumentaciju i odgovor na incidente.
Bez vlasništva zajednička infrastruktura postaje odlagalište. Zajednički paket u početku može biti praktičan, ali može neprimjetno povezati implementacije. Zajednička baza podataka još je opasnija: jedna usluga može promijeniti tablicu ili obrazac upita i neočekivano pokvariti drugu.
Poželjno je da svaka usluga posjeduje svoje podatke. Druge usluge trebale bi im pristupati putem stabilnog API-ja ili događaja, a ne izravnim spajanjem njezinih tablica. To se može činiti sporijim od upita između baza podataka, ali čuva mogućnost sigurnog mijenjanja shema.
Odaberite odgovarajuću vrstu ugovora
Upotrebljavajte sinkrone HTTP API-je kada pozivatelj treba trenutačan odgovor, primjerice za potvrdu može li kupac naručiti. Upotrebljavajte asinkrone događaje kada proizvođač ne bi trebao čekati daljnji rad, primjerice za obavještavanje analitike nakon prihvaćanja narudžbe.
Nijedan stil ne uklanja potrebu za ugovorima. API ugovor trebao bi definirati validaciju zahtjeva, oblike odgovora, statusne kodove, autentikaciju i ponašanje pri pogreškama. Ugovor o događaju trebao bi definirati njegovu shemu, pristup verzioniranju, pretpostavke isporuke i što potrošači moraju učiniti kada stigne duplikat.
{
"event": "order.placed",
"event_id": "a1b2c3",
"occurred_at": "2026-08-10T12:00:00Z",
"data": {
"order_id": "ord_123",
"customer_id": "cus_456",
"total": 49.99,
"currency": "USD"
}
}
Potrošač bi event_id trebao tretirati kao ključ idempotentnosti. Isporuka barem jednom uobičajena je u distribuiranim sustavima, stoga je „obradi svaku poruku točno jednom” rijetko sigurna pretpostavka. Pohranite dovoljno stanja da otkrijete ponavljanje prije slanja e-pošte, primjene kredita ili stvaranja pošiljke.
Dizajnirajte za kvar prije nego što nastupi
Unutar monolita poziv funkcije ili uspije ili brzo izazove pogrešku. Preko mreže zahtjev može isteći dok udaljena usluga nastavlja obradu. Klijent ga može ponovno pokušati, red ga može duplicirati ili može biti odbijen jer je ovisnost preopterećena.
Vremenska ograničenja moraju biti namjerna. Zahtjev bez vremenskog ograničenja može zauzimati radnike dok sustav ne postane nedostupan. Ponovni pokušaji trebaju ograničenja, odgodu i jasan odgovor na jedno pitanje: je li postupak sigurno ponoviti?
$response = $client->request('POST', '/payments', [
'json' => $payload,
'headers' => [
'Idempotency-Key' => $paymentAttemptId,
],
'timeout' => 3.0,
]);
Ključ idempotentnosti ne čini ponovne pokušaje magično sigurnima. Usluga za primanje plaćanja mora ga pohraniti i poštovati. Za ponovljeni ključ trebala bi vratiti izvorni rezultat umjesto da ponovno naplati kupcu.
Također razlikujte privremeni kvar od valjanog poslovnog ishoda. Odbijeno plaćanje nije infrastrukturna pogreška koju treba ponovno pokušati. Istek vremena može se moći ponovno pokušati, ali samo unutar ograničenog pravila. Kada je naknadna ovisnost nedostupna, vratite smislen odgovor, po potrebi stavite rad u red za kasnije ili degradirajte nebitnu funkcionalnost umjesto da dopustite kaskadno širenje kvara.
Neka API-ji budu mali i vidljivi
Brbljava komunikacija među uslugama jedan je od najbržih načina da se čist dijagram pretvori u sporu aplikaciju. Ako prikaz stranice narudžbe zahtijeva deset uzastopnih mrežnih poziva, latencija se nakuplja, a pouzdanost opada sa svakom ovisnošću.
Dajte prednost krupnozrnatim krajnjim točkama koje odgovaraju stvarnoj potrebi klijenta. Kada tijek rada zahtijeva podatke iz nekoliko domena, razmotrite namjenski sloj za sastavljanje ili model za čitanje izgrađen iz događaja. Nemojte svaki problem čitanja rješavati sinkronim razgranavanjem.
Jednako je važna i vidljivost. U najmanju ruku, propagirajte ID korelacije kroz dolazne zahtjeve, odlazne HTTP pozive, zapisnike i asinkrone poruke. Bilježite strukturirani kontekst: naziv usluge, ID zahtjeva, rutu, status, trajanje i relevantne identifikatore resursa. Izbjegavajte bilježenje tajni, autorizacijskih tokena ili potpunih osobnih podataka.
- Mjerite stopu zahtjeva, stopu pogrešaka i latenciju za svaku javnu ovisnost.
- Pratite dubinu reda, starost najstarije poruke i obradu neuspjelih poruka za asinkrone tijekove rada.
- Upozoravajte na simptome koje korisnici doživljavaju, a ne na svaki bučni interni događaj.
- Neka nadzorne ploče budu usmjerene na ključne tijekove rada usluge, a ne na svaku dostupnu metriku.
Koristite Docker kako bi okruženja bila predvidljiva
Spremnici pomažu kada lokalni razvoj, testiranje i implementaciju čine dosljednijima. Ne zamjenjuju dobro upravljanje konfiguracijom ni operativnu disciplinu. Izgradite nepromjenjivu sliku, pri izvršavanju ubrizgajte konfiguraciju specifičnu za okruženje te tajne držite izvan slike i izvornog repozitorija.
FROM php:8.3-cli
WORKDIR /app
COPY . .
RUN docker-php-ext-install pdo_mysql
CMD ["php", "bin/console", "messenger:consume", "async"]
Važna arhitektonska točka nije točna osnovna slika. Važno je da radnik ima jednu jasnu odgovornost, da je njegova konfiguracija izdvojena i da se njegovo stanje može promatrati. Migracije baze podataka izvršavajte kao kontrolirani korak implementacije, a ne kao neispitanu nuspojavu svakog pokretanja spremnika. Promjene sheme trebale bi ostati kompatibilne i sa starom i s novom verzijom aplikacije tijekom postupne implementacije.
Ne miješajte neovisnost s izolacijom
Neovisna implementacija vrijedna je samo kada je sigurna. Usluga koja mora koordinirati izdanja s njih pet nije smisleno neovisna, čak i ako svaka ima vlastiti repozitorij i Docker sliku. Pažljivo verzionirajte API-je, najprije unosite aditivne promjene te uklanjajte stara polja ili krajnje točke tek nakon što potrošači migriraju.
Jednako tako, zasebna usluga ne treba zaseban tehnološki skup. Standardizacija na PHP-u, zajedničkom formatu zapisnika, zajedničkim konvencijama implementacije i malom skupu odobrenih infrastrukturnih obrazaca smanjuje kognitivno opterećenje. Autonomija bi trebala omogućiti bolje odluke, a ne prisiljavati svaki tim da ponovno otkriva iste operativne osnove.
Zaslužite složenost konkretnom koristi
Najpragmatičnija arhitektura mikrousluga često je manja nego što njezin prvi dijagram sugerira. Počnite s dobro definiranim modulima, eksplicitnim sučeljima i discipliniranim vlasništvom nad podacima. Izdvojite uslugu kada postoji stvaran razlog da njome upravljate zasebno, a ne zato što organizacija želi izgledati distribuirano.
Mikrousluge uspijevaju kada granice čine promjene sigurnijima, kvarove ograničenijima, a vlasništvo jasnijim. Kada samo premještaju čvrsto povezani kod preko mreže, pojačavaju zbrku. Svaku novu uslugu tretirajte kao dugoročnu operativnu obvezu i arhitektura će ostati alat za isporuku softvera, umjesto da postane proizvod na čije održavanje trošite sve vrijeme.