Bu qatlamda nima bor
Core darslik asoslarni berdi. Bu Deep qatlam senior darajadagi chuqur mexanikani ochadi: projection idempotency va checkpoint, read model rebuild/replay, bir nechta maxsus projection, o‘qish/yozishni mustaqil masshtablash, command validatsiya va optimistik concurrency, eventual consistency UX naqshlari va Event Sourcing bilan birlashtirish. Oxirida — yig‘ma arxitektura va senior cheklist.
Idempotency va checkpoint
at-least-once · upsert · checkpointSenior haqiqat: Core’da projection’ni event yangilaydi dedik. Lekin eventlar (3-mavzu) at-least-once yetkaziladi — bir event ikki marta kelishi mumkin. Projection bunga tayyor bo‘lishi shart, aks holda read model buziladi.
1. Duplikat: bir event ikki marta qayta ishlansa — masalan, hisoblagich ikki marta oshib ketadi (noto‘g‘ri).
2. Tartib: eventlar tartibi buzilib kelishi mumkin (ayniqsa, ko‘p partition).
Yechim — idempotency + checkpoint
Har projection o‘zining checkpointini (oxirgi qayta ishlangan event pozitsiyasi) yuritadi va allaqachon ko‘rgan eventni tashlab ketadi. Yozish esa upsert (takror ishlansa holat bir xil qoladi):
// IDEMPOTENT PROJECTION: event AT-LEAST-ONCE yetkaziladi (3-mavzu) -> takror kelishi mumkin
@EventsHandler(OrderCreatedEvent)
export class OrderProjection implements IEventHandler<OrderCreatedEvent> {
async handle(e: OrderCreatedEvent) {
const cp = await this.getCheckpoint('orderView');
if (e.position <= cp) return; // allaqachon ishlangan -> SKIP
// upsert (insert YOKI update) -> takror ishlansa ham xavfsiz, holat bir xil
await this.prisma.orderView.upsert({
where: { orderId: e.orderId },
create: { orderId: e.orderId, userId: e.userId, total: e.total, version: e.version },
update: { total: e.total, version: e.version },
});
await this.saveCheckpoint('orderView', e.position); // checkpoint'ni oldinga sur
}
}
Hatto checkpoint bilan ham, "yozdim-u checkpoint saqlashdan oldin yiqildim" holati bor. Upsert (yoki idempotent yozuv) buni xavfsiz qiladi: event qayta kelsa ham, natija o‘zgarmaydi. Idempotency — projection’ning eng muhim sifati.
Read model replay / qayta qurish
replay · blue-greenCQRS’ning eng kuchli imkoniyati — read modelni xohlagancha qayta qurish. Read model — shunchaki eventlarning hosilasi; xohlagan paytda o‘chirib, qayta tiklash mumkin.
Nega bu super-kuch
Read model — "haqiqat" emas, balki eventlarning keshlangan ko‘rinishi. Demak:
- Projection’da bug bo‘lsa — tuzatib, read modelni qayta qurasan (ma’lumot yo‘qolmaydi).
- Yangi read model shakli kerak bo‘lsa — eski eventlarni qayta o‘ynatib, yangisini to‘ldirasan.
- Schema o‘zgarsa — yangi sxemaga qayta quriladi.
// REBUILD: read modelni NOLDAN qayta qurish (projection'da bug, yangi shakl, schema)
async rebuildOrderView() {
await this.prisma.orderView.deleteMany(); // 1) eski read modelni tozala
await this.resetCheckpoint('orderView'); // 2) checkpoint -> 0
for await (const event of this.eventStore.readAll()) { // 3) HAMMA eventni qayta o'ynat
await this.projection.handle(event); // -> read model qaytadan quriladi
}
}
Read modelni jonli almashtirish: yangi versiyasini yon tomonda qurib boshlaysan, u eventlarga yetib olgach (caught up), o‘qishni unga atomik o‘tkazasan, eskisini o‘chirasan. Foydalanuvchi uzilish sezmaydi (5-mavzudagi blue-green deploy g‘oyasi).
Rebuild faqat eventlar saqlangan bo‘lsa mumkin — Event Sourcing (10-mavzu), yoki event store / Kafka yetarli retention bilan. Eventlar yo‘q bo‘lsa (faqat joriy holat saqlangan) — qayta qurish uchun manba yo‘q.
Bir nechta maxsus projection
polyglot persistenceBitta yozuv modeli — ko‘p read model. Har biri boshqa maqsadga, boshqa omborda optimallashtirilgan. Bu CQRS’ning amaliy kuchi.
Bir xil eventlar oqimidan bir nechta mustaqil projection o‘z read modelini quradi:
| Read model | Ombor | Maqsad |
|---|---|---|
| Ro‘yxat / detal | SQL view jadval | Tez, JOIN’siz sahifalar |
| Qidiruv | Elasticsearch | To‘liq-matnli, murakkab filtr |
| Hot lookup | Redis | Eng tez kalit bo‘yicha o‘qish |
| Hisobotlar | Oldindan hisoblangan agregat | Dashboard, analitika |
Har projection o‘z checkpointi, o‘z rebuildiga ega. Bittasi buzilsa — boshqalarga ta’sir qilmaydi. Yangi read model kerak bo‘lsa — istalgan paytda eventlarni qayta o‘ynatib qo‘shasan, yozuv tomoniga tegmasdan.
O‘qish/yozishni mustaqil masshtablash
independent scalingCQRS’ning arxitektura mukofoti: o‘qish va yozish mustaqil masshtablanadi. Ular endi bir-biriga bog‘liq emas — har biri o‘z yukiga qarab o‘sadi.
Real ilovalarda o‘qish yozishdan 10–100 barobar ko‘p. CQRS bilan bu nomutanosiblikni to‘g‘ridan-to‘g‘ri hal qilasan:
- Yozuv tomoni — izchillik uchun optimallashtiriladi: single-writer, transaksiya, kam nusxa.
- O‘qish tomoni — throughput uchun: ko‘p nusxa, kesh (4-mavzu), o‘qish replikasi (7-mavzu), hatto alohida sharding (6-mavzu) — yozuv modeliga tegmasdan.
Bu — oldingi mavzularning birlashishi: read modellarini keshlash (4), o‘qish replikasi (7), kerak bo‘lsa sharding (6) — barchasi faqat o‘qish tomonida, yozuv tomonini murakkablashtirmasdan.
Validatsiya va concurrency
optimistic concurrency · idempotencyYozuv tomoni — faqat "saqlash" emas. Senior darajada bu yerda validatsiya qatlamlari va concurrency (parallel o‘zgarishlar) boshqariladi.
Command validatsiya — ikki qatlam
1. Kirish validatsiyasi (DTO darajasi): maydonlar to‘g‘ri turdami, bo‘shmi (class-validator). 2. Biznes invariant (aggregat darajasi): "buyurtmani yopilganidan keyin o‘zgartirib bo‘lmaydi", "balans manfiy bo‘lmasin". Birinchisi shaklni, ikkinchisi qoidalarni tekshiradi.
Optimistic concurrency — yo‘qolgan yangilanish
Ikki command bir buyurtmani bir vaqtda o‘zgartirsa: ikkalasi ham eski holatni o‘qiydi, ikkalasi yozadi — ikkinchisi birinchisini jimgina yo‘q qiladi. Pul/inventar bilan bu halokat.
Yechim — versiya bilan optimistik concurrency: yozishda kutilgan versiya o‘zgarmaganligini tekshir, o‘zgargan bo‘lsa rad et:
// OPTIMISTIC CONCURRENCY: ikki command bir aggregatni o'zgartirsa -> lost update
async execute(cmd: UpdateOrderCommand) {
const order = await this.repo.findById(cmd.orderId); // hozirgi version = 5
order.applyChanges(cmd); // domen logikasi + invariant
// YOZISHDA versiyani tekshir: kutilgan versiya o'zgarmaganda GINA yoz
const res = await this.prisma.order.updateMany({
where: { id: cmd.orderId, version: 5 }, // version hali 5 bo'lsa
data: { ...order.state, version: 6 }, // 6 ga oshir
});
if (res.count === 0) // kimdir oraliqda o'zgartirdi
throw new ConflictException('Concurrent update — qayta urin');
}
Mijoz so‘rovni qayta yuborsa (retry, 14-mavzu) — command ikki marta bajarilmasin. Har commandga command_id ber va qayta ishlashdan oldin "bu id bajarilganmi?"ni tekshir (dedup). Bu — yozuv tomonidagi idempotency (projection idempotency’ning yozuv juftligi).
Eventual consistency naqshlari
optimistic UI · versioned readCore’da eventual consistency’ni ko‘rdik. Senior darajada uni foydalanuvchi uchun sezilmaydigan qilish — UX naqshlari masalasi.
Foydalanuvchi lag’ni sezmasligi uchun
- Optimistik UI: foydalanuvchining o‘z amalini darhol ekranda ko‘rsat (server tasdig‘ini kutmasdan), fonda haqiqiy holatga moslashtir.
- Versioned read: command yangi versiya raqamini qaytaradi; o‘qishda read model shu versiyaga yetgunicha qisqa kut (yoki write model’dan fallback).
- "Pending" holat: "Buyurtmangiz qayta ishlanmoqda…" — foydalanuvchi nima bo‘layotganini biladi.
- Push/subscribe: projection yetib olganda WebSocket orqali "tayyor" signali yubor.
// VERSIONED READ: command yangi versiyani qaytaradi; o'qishda read model
// shu versiyaga yetgunicha biroz kutamiz (read-your-writes UX)
async getOrderView(orderId: string, minVersion: number) {
for (let i = 0; i < 10; i++) {
const view = await this.prisma.orderView.findUnique({ where: { orderId } });
if (view && view.version >= minVersion) return view; // projection yetdi -> ber
await sleep(50); // biroz kut (max ~500ms)
}
return this.readFromWriteModel(orderId); // fallback: write model'dan
}
Ba’zi joylarda (to‘lov tasdig‘i) eventual consistency joiz emas. Bunda o‘sha bir read modelni sinxron yangilash mumkin (command bilan bitta transaksiyada) — kechikishni boshqa modellarning asinxronligi bilan qoplab. Hammasi async bo‘lishi shart emas.
Event Sourcing bilan birlashtirish
CQRS + ESCQRS va Event Sourcing (ES) — tez-tez birga aytiladi, lekin mustaqil. Ularning munosabatini aniq tushunish senior belgisi.
Ular nima va nega mos
Event Sourcing (10-mavzu) — joriy holat o‘rniga barcha eventlarni haqiqat manbai sifatida saqlash. CQRS bilan birlashganda ideal:
- Yozuv tomoni = event oqimi (ES) — har o‘zgarish event sifatida saqlanadi.
- O‘qish tomoni = o‘sha eventlardan qurilgan projection’lar.
- Rebuild (2-bo‘lim) tabiiy bo‘ladi — eventlar allaqachon saqlangan. To‘liq audit/tarix bepul keladi.
CQRS ES’ni talab qilmaydi: projection’larni oddiy holat-o‘zgarish eventlaridan yoki CDC’dan (7-mavzu logical replication) ham qurish mumkin. ES ham CQRS’ni talab qilmaydi. Ikkalasi birga kuchli, lekin har biri alohida ham ishlatiladi. Ikkalasini birga olish — katta murakkablik, faqat haqiqiy ehtiyojda.
Yig‘ma ko‘rinish va senior cheklist
To‘liq CQRS arxitekturasi — yig‘ma ko‘rinish
| Qism | Vazifa | Bo‘lim |
|---|---|---|
| Command + validatsiya + optimistik concurrency | Izchil yozuv | 5 |
| Event (yozuv natijasi) | O‘qish tomonini xabardor qiladi | 1 |
| Idempotent projection + checkpoint | Ishonchli read model qurish | 1 |
| Bir nechta read model (SQL/ES/Redis) | Har maqsadga optimal o‘qish | 3 |
| Rebuild / replay | Bug tuzatish, yangi shakl, schema | 2 |
| Eventual consistency UX | Lag’ni foydalanuvchidan yashirish | 6 |
Senior tayyorlik cheklisti
| Savol / amaliyot | Bo‘lim |
|---|---|
| Projection’ni idempotent + checkpoint bilan quraman (at-least-once’ga chidamli) | 1 |
| Read modelni replay bilan qayta quraman; blue-green almashtiraman | 2 |
| Bir event oqimidan bir nechta maxsus read model quraman | 3 |
| O‘qish va yozishni mustaqil masshtablayman (kesh/replika/sharding o‘qishda) | 4 |
| Command’da ikki qatlam validatsiya + optimistik concurrency + idempotency qo‘llayman | 5 |
| Eventual consistency’ni optimistik UI / versioned read / sinxron projection bilan boshqaraman | 6 |
| CQRS va ES munosabatini bilaman; qachon birga, qachon alohida | 7 |
Bu 9-mavzuning Deep/Senior qatlami edi — Core bilan birga, CQRS endi to‘liq.
Keyingi mavzu: “10-mavzu: Event Sourcing” — joriy holat o‘rniga barcha eventlarni haqiqat manbai sifatida saqlash (CQRS’ning tabiiy jufti). Core’dan boshlaymiz.