CQRS implementacija razdvaja put kojim se podaci mijenjaju od puta kojim se čitaju. Komande nose namjeru („potvrdi ovu narudžbu”, „odobri ovu isplatu”) i sprovode pravila. Upiti vraćaju tačno oblik koji trebaju ekran, izvještaj ili polje za pretragu, i ne moraju ličiti na write model. Podjela je odgovor na konkretan bol: pisanje puno politike, i čitanje koje je sporo jer stalno spaja tabele koje tim politikama trebaju.
Sadržaj
U CodeWizu tu podjelu uvodimo u Laravel, Node.js i u postojeći modularni kod. Dizajniramo command handlere, query handlere i, tamo gdje je sama prošlost proizvod, event store. Uz to ostaju testovi koji pravilo izgovaraju bez HTTP-a i put da se read model ponovo izgradi kad se raziđe sa izvorom. Kažemo i ne. CRUD admin panel sa šačicom validacija ne postaje bolji zato što je svako dugme preimenovano u komandu, a svaki dodatni projektor je nešto što neko mora da nadgleda.
Usluga je za naplatu, rezervacije, marketplace, zalihe i multi-tenant proizvode u kojima je pogrešan prelaz stanja skup, i za ekrane koji se uglavnom čitaju (dashboarde, pretragu, javne kataloge) a guše bazu složenu za pisanje. Ako su obje strane jednostavne, ostavljamo jedan model.
Kod teme cqrs implementacija alati otvorenog koda su prvi izbor kada odgovaraju poslu.

Command handleri koji sprovode poslovna pravila
Komanda je zahtjev da se uradi jedna stvar, imenovana jezikom poslovanja: PlaceOrder, RefundPayment, SuspendTenant. Command handler učita aggregate ili zapis koji mu treba, pozove ponašanje koje provjerava pravila i sačuva rezultat. Ne slaže prikaz i ne vraća stranicu podataka. Uspjeh je „izmjena je prihvaćena”. Neuspjeh je domenska greška koju pozivalac može pokazati ili ponoviti.
CQRS Implementacija kreće od sistema koji već radi, a ne od praznog šablona.
Šta pripada handleru, a šta ne
- Provjera da je komanda dobro formirana (ID-evi prisutni, iznosi pozitivni) dešava se prije handlera, na ivici.
- Poslovna pravila (prazna narudžba ne može da se potvrdi, povrat ne može preći naplaćeni iznos) žive na modelu koji handler zove, tako da drugi ulaz ne može da ih preskoči.
- Handler je idempotentan tamo gdje poslovanje to traži. Ponovni pokušaj klijenta za
PlaceOrdersa istim ID-em ne smije napraviti drugu narudžbu. - Nuspojave koje mogu pasti nezavisno (email, naplata, search indeks) bilježe se i obrađuju tek nakon što se izmjena stanja potvrdi u bazi, a ne unutar iste krhke sekvence.
Sam handler ostaje tanak. Ovo je oblik koji koristimo u PHP-u; TypeScript verzija je ista ideja sa drugom sintaksom:
Rad na temi cqrs implementacija ostaje u vašem repozitoriju i na vašoj infrastrukturi.
final class PlaceOrderHandler
{
public function __construct(
private OrderRepository $orders,
private Clock $clock,
) {}
public function handle(PlaceOrder $command): void
{
$order = $this->orders->get($command->orderId);
$order->place($this->clock->now());
$this->orders->save($order);
}
}Pošto pravilo stoji na Order::place, testovi ga pokrivaju bez HTTP-a, a queue worker može zvati isti handler koji zove API. Fowlerova bilješka o CQRS-u je kratko objašnjenje koje šaljemo prije razgovora o dizajnu, da se soba prepire oko podjele, a ne oko akronima.
CodeWiz provjerava cqrs implementacija u produkciji, ne samo na demonstraciji.
Query handleri i read modeli složeni za ekran
Query handler odgovara na pitanje i ne mijenja ništa. OrderDetails, OpenInvoicesForAccount i SearchCatalog svaki vraća read model: DTO složen za jednog potrošača. Handler može pokrenuti SQL, čitati Redis ili zvati Typesense. Ne učitava aggregate i ne šeta njegov graf objekata da bi nacrtao tabelu.
Odluke o temi cqrs implementacija vežu se za ograničenje koje stvarno koči poslovanje.
Read modeli smiju biti dosadni
- Jedan read model po ekranu ili API odgovoru. Dashboard ne preuzima write-side repository „da ostane DRY”.
- Kolone su unaprijed spojene i formatirane. Cijena koju UI prikazuje sačuvana je onako kako UI-ju treba, sa simbolom valute ako ekran baš to crta.
- Indeksi prate upit, ne granicu aggregatea. Izvještaj koji filtrira po tenantu i statusu dobija taj indeks čak i ako write model narudžbe nikada ne traži na taj način.
- Rebuild je mogućnost, ne incident. Ako je projekcija pogrešna, izvor istine može se ponovo odvrti u prazan read model umjesto ručnog ispravljanja redova.
Pretraga je read model kao i svaki drugi. Typesense kolekcija koju hrani write strana često je bolji katalog od gomile SQL LIKE upita, i transakcionu bazu sklanja sa puta autocompletea.
Predaja teme cqrs implementacija uključuje uputstvo koje tim može koristiti.

Event store: zapis onoga što se zaista desilo
Event store je append-only log domain eventova, po redu, po aggregateu. Umjesto da čuvamo samo trenutni red, čuvamo OrderPlaced, PaymentCaptured, LineRefunded. Trenutno stanje je presavijanje te prošlosti: isti događaji, pročitani redom, uvijek daju isti rezultat. Store je izvor istine za write stranu; read modeli su projekcije izgrađene iz istih eventova, i smiju kasniti dok se write strana ne složi sa pravilima.
Za cqrs implementacija CodeWiz prvo mjeri ograničenje, pa bira alat.
Šta upisujemo
- Ime eventa na ubiquitous languageu, verziju šeme, ID aggregatea, monotono rastuću verziju i payload činjenica, a ne diff svake kolone „za svaki slučaj”.
- Metapodatke koji trebaju operacijama: ko je izazvao, koji command ID, kada se desilo po satu aplikacije.
- Optimističku konkurentnost na verziji aggregatea, tako da dva handlera ne mogu dopisati jedan preko drugog i u tišini odbaciti pravilo.
- Pretplatu ili polling projektor koji ažurira read modele, sa checkpointom da restart nastavi, a ne da krene ispočetka.
Za poseban store (EventStoreDB, ili PostgreSQL tabela složena kao append-only log) posežemo kad je prošlost zahtjev: revizija, „kako je ovaj nalog izgledao u utorak”, ili nekoliko potrošača kojima svakom treba svoj oblik. Ne uvodimo ga zato što je CQRS pomenut na sastanku. Azureove arhitektonske bilješke o CQRS obrascu prave istu razliku: razdvajanje čitanja i pisanja ne traži čuvanje eventova.
CQRS Implementacija ostaje prilagođen poslu, a ne obrnutno.
Tamo gdje je gubitak koraka neprihvatljiv, event je ono što odbijamo da preskočimo. Isti je to nagon kao kad se online prodavnica složi tako da se svaka radnja kupca može rekonstruisati, o čemu smo pisali u tekstu o čuvanju narudžbi i radnji kupca.
CQRS Implementacija je tema ovog članka, od ograničenja koje koči do predaje posla.

Razdvajanje čitanja i pisanja u CQRS implementaciji
Razdvajanje čitanja i pisanja je skala, ne prekidač. CQRS implementacija može biti dogovor o imenima unutar jedne baze, ili dva enginea sa projektorom između. Biramo najjeftiniji nivo koji uklanja stvarni bol, i ostavljamo prostor da se popne stepenik više bez prepisivanja komandi.
| Nivo | Šta je razdvojeno | Isplati se kad | Preuzimate |
|---|---|---|---|
| Logička podjela | Command i query klase, jedna baza, jedna šema | Pravila se gube u kontrolerima, a čitanje je još u redu | Vrlo malo infrastrukture |
| Namjenski read upiti | Write model ostaje normalizovan; ekrani dobijaju svoj SQL ili viewove | Nekoliko stranica dominira bazom zbog joinova | Dva načina čitanja istih činjenica, koje testovi drže iskrenim |
| Projektovani read store | Projektor puni Redis, Typesense ili read šemu | Pretraga, dashboardi ili javni saobraćaj ne smiju udarati write bazu | Kašnjenje, rebuildove i nadzor projektora |
| Event-sourced pisanje | Log je write model; svaki read model je projekcija | Revizija, putovanje kroz vrijeme ili mnogo nezavisnih potrošača jesu zahtjev | Verzionisanje, replay i stroži pristup izmjeni šeme |
Jedna baza dok opterećenje ne kaže drugačije
Većini proizvoda koje vidimo treba prvi ili drugi red. Komande i upiti postaju različiti tipovi, write strana dobija manje transakcije, a težak ekran dobija upit koji ne učitava aggregate. Druga baza se pojavljuje kad read opterećenje, zahtjevi pretrage ili broj potrošača opravdaju rad projektora. Skaliranje same write baze, particioniranjem ili shardingom, zasebna je odluka o kojoj smo pisali u vodiču za sharding i particioniranje. CQRS taj posao ne zamjenjuje; često samo učini šav očiglednim.
Konzistentnost, projekcije i ono što korisnik vidi
Čim se read model ažurira asinhrono, korisnik može da upiše izmjenu i da je odmah ne vidi. To nije bag koji treba sakriti. To je odluka o proizvodu, sa budžetom u milisekundama ili sekundama, i sa interfejsom koji se ne pretvara da je lista trenutno potpuna. Katalog koji kasni sekundu je u redu. Stanje novca na računu koje kasni minut nije, i taj ekran čita write stranu.
- Nakon komande, UI za taj jedan odgovor može čitati iz write modela (narudžba koja je upravo potvrđena), a projekciju koristiti za liste.
- Upozorenje na kašnjenje projektora pali se prije nego što kupci primijete, po starosti checkpointa, ne po maglovitom „red izgleda zauzeto”.
- Projekcije se mogu ponovo izgraditi iz izvora istine, u probnom prolazu čije trajanje jesmo izmjerili.
- Komande koje se ne smiju preurediti (naplata, pa povrat) nose verziju aggregatea, tako da write strana ostaje sekvencijalna i kad čitaoci kasne.
Komande koje diraju novac
Tokovi plaćanja su mjesto gdje aljkav model komandi postane dupla naplata ili povrat koji nedostaje. Capture, void i refund tretiramo kao komande sa vlastitim idempotency ključevima, a referencu providera čuvamo uz domain event, tako da se ishod može objasniti i sedmicu kasnije bez čitanja tuđeg dashboarda. Integracija gatewaya ostaje na ivici: handler traži od porta da naplati, a event bilježi ishod. Ponavljanja su sigurna jer isti command ID vraća isti rezultat umjesto da providera zove ponovo. Okolni integracioni posao opisali smo u tekstu o integraciji payment gatewaya za online prodavnice.
Česte greške u CQRS implementaciji koje ispravljamo
- Command handleri koji usput slažu i response DTO, pa je „podjela” samo ime foldera.
- Read model koji niko ne može ponovo izgraditi, ručno dorađivan dok se ne raziđe sa write stranom.
- Eventi po imenu
OrderUpdatedsa cijelim redom unutra, koji se ne mogu verzionisati i poslovanju ne znače ništa. - Dva upisa u jednoj komandi (narudžba i faktura i email) bez plana za onaj koji padne na sredini.
- Uveden event store, a mutable red i dalje tretiran kao izvor istine.
- Pretpostavka da svaki upit mora biti konzistentan do milisekunde, pa se grade sinhroni projektori koji vrate prvobitnu spregu.
Kako radimo
- Upoznavanje: popisujemo prelaze stanja koji nose pravila i ekrane koje je sporo ili nezgodno pitati.
- Nivo: biramo dokle podjela ide, od razdvojenih handlera do projektovanog read storea ili event loga, i zapisujemo budžet konzistentnosti.
- Model: komande, aggregatei i eventi koje vrijedi bilježiti, sa imenima iz poslovanja, a ne iz kolona tabele.
- Izrada: handleri, write put, projektori i read upiti, plus idempotency ključevi na svemu što naplaćuje novac ili šalje poruku.
- Dokaz: testovi za pravila, vremenski izmjerena ponovna izgradnja svake projekcije i test ponavljanja koji pokazuje da dupla komanda ne duplicira efekat.
- Podrška: dashboardi za kašnjenje projektora i pale komande, i zapisan način da se sljedeća komanda doda bez izmišljanja novog obrasca.
Zašto CodeWiz
- Isporučujemo kod: handleri, projektori i migracije ulaze u vaš Laravel ili Node.js repozitorij, ne u slajd koji imenuje obrazac.
- Najmanja podjela koja radi: nećemo prodati event-sourced platformu za problem koji bi riješile razdvojene query klase.
- Open-source na prvom mjestu: PostgreSQL, Redis, Typesense i, kad je log opravdan, EventStoreDB. Nema naknade po eventu kao ulaznice.
- Full-stack: ekran, API i write model nastaju zajedno, tako da read model odgovara onome što interfejs zaista crta.
- Fer cijene: vrhunski razvoj bez vrhunskih cijena, od tima iz Banje Luke koji radi na daljinu sa klijentima iz regiona i svijeta. Više o CodeWizu.
Često postavljana pitanja
Koliko košta CQRS implementacija?
Podjela handlera unutar jedne aplikacije, uz par namjenskih read upita, skroman je projekat: tipovi, testovi i jedan ekran koji više ne vuče aggregate. Event store, nekoliko projekcija i put za rebuild živog proizvoda veći su posao, najviše zbog migracije i vremena potrošenog na dokaz replaya, uključujući i probu da se projekcija izgradi ispočetka na kopiji podataka. Procjenu dajemo kad vidimo write pravila i ekrane koji bole.
Da li mi treba event sourcing da bih koristio CQRS?
Ne. Event sourcing je jedan način da se uradi write strana, koristan kad je prošlost zahtjev. Mnoge CQRS baze koda drže običan relacioni write model i razdvajaju samo upite. Tu počinjemo, osim ako revizija ili više nezavisnih potrošača event sourcingu daju posao.
Hoće li korisnici vidjeti zastarjele podatke?
Samo ako se read model ažurira asinhrono, i samo onoliko koliko projektoru treba. Taj budžet postavljamo eksplicitno, korisniku zapis koji je upravo promijenio pokazujemo sa write strane i palimo upozorenje na kašnjenje. Logička podjela u jednoj bazi nema dodatnog kašnjenja.
Možete li dodati CQRS u postojeću Laravel ili Node.js aplikaciju?
Možemo. Obično počnemo od jedne komande i jednog bolnog upita, iza postojećeg API-ja, a ostatak CRUD-a ostavimo na mjestu. Obrazac se širi kako još pravila i još ekrana to zasluži. Prepisivanje u novi servis rijetko je prvi korak.
Da li je CQRS samo za mikroservise?
Ne. Podjela je o modelima, ne o deployable jedinicama. Modularnom monolitu posebno pristaje: komande i upiti su tipovi u jednom kodu, a distribuirane transakcije izbjegavate. Zasebni servisi dolaze kasnije, ako vlasništvo tima ili opterećenje to traže.
Spremni da odvojite pravila od ekrana?
Recite nam koje radnje nose prava pravila i koji ekrani savijaju bazu. Predložićemo nivo podjele koji odgovara proizvodu, a ne reputaciji obrasca. Kontaktirajte nas da prođemo kroz to, ili pogledajte naše usluge CQRS implementacije.