Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. CQRS nima
- 2.2. Command va Query ajratish
- 2.3. Event Sourcing nima
- 2.4. Holatni hodisalardan tiklash
- 2.5. Snapshot va optimizatsiya
- 2.6. CQRS va Event Sourcing birga
- 2.7. Qachon kerak (va murakkabligi)
- 2.8. CQRS/Event Sourcing — haqiqat sifatida hodisalar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — CQRS: command va query ajratish
- Misol 2 — Event Sourcing: hodisalardan holat
- Misol 3 — O'tmishga qaytish va yangi ko'rinish
- Misol 4 — Snapshot (optimizatsiya)
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
27.11-dars: CQRS va Event Sourcing
27-QISM — ARXITEKTURA · 11-dars
1. Kirish va motivatsiya
An'anaviy tizimda bir model ham o'qish, ham yozish uchun ishlatiladi: bir User sinfi orqali ma'lumot yoziladi va o'qiladi. Lekin o'qish va yozish ehtiyojlari juda farq qiladi: yozish murakkab qoidalar va tekshiruvlarni talab qiladi (izchillik), o'qish esa tez va turli ko'rinishlarni (hisobot, ro'yxat, qidiruv). Bir model ikkalasiga xizmat qilsa — murosaga boradi (yozish uchun optimal — o'qish uchun sekin). CQRS o'qish va yozishni ajratadi. Yana bir muammo: an'anaviy tizim faqat joriy holatni saqlaydi (balans = 100), o'tmish yo'qoladi (balans qanday 100 bo'ldi?). Event Sourcing holatni hodisalar ketma-ketligi sifatida saqlaydi — to'liq tarix.
CQRS (Command Query Responsibility Segregation — buyruq-so'rov mas'uliyatini ajratish) — yozish (command — holatni o'zgartiradi) va o'qish (query — holatni qaytaradi) uchun alohida modellar: yozish modeli qoidalarga, o'qish modeli tezlikka optimallashtiriladi. Event Sourcing — tizim holatini emas, unga olib kelgan hodisalar ketma-ketligini saqlash: joriy holat hodisalarni "qayta o'ynash" bilan hisoblanadi. Ular hodisaga asoslangan arxitektura 27.10-bob bilan chuqur bog'liq (hodisalar manba). Bu murakkab domenlar uchun kuchli, lekin murakkab naqshlar. CQRS/Event Sourcing — o'qish/yozish ajratish va tarix saqlash.
Real vaziyat. Bir bank tizimida hisob balansini ko'rsatish sekin edi (murakkab yozish modeli o'qish uchun ham ishlatilardi) va "balans qanday shakllandi?" degan savolga javob yo'q edi (faqat joriy balans). CQRS qo'llanildi: yozish (pul o'tkazish — qoidalar) va o'qish (balans, tarix ko'rsatish — tez) ajratildi. Event Sourcing: har tranzaksiya hodisa sifatida saqlandi ("100 kiritildi", "30 yechildi") — balans hodisalardan hisoblanadi, to'liq tarix mavjud. Audit va tezlik yaxshilandi.
Bu darsda CQRS va Event Sourcing ni o'rganamiz.
Bu darsda:
- CQRS nima
- Command va Query ajratish
- Event Sourcing nima
- Holatni hodisalardan tiklash
- Snapshot va optimizatsiya
- CQRS va Event Sourcing birga
- Qachon kerak (va murakkabligi)
- Amaliy: hodisalardan holat
ℹ Misollar sof Python bilan — hodisa saqlash xotirada (deterministik).
2. Nazariya — chuqur tushuntirish
2.1. CQRS nima
O'qish va yozishni ajratish:
An'anaviy: bir model (o'qish + yozish)
CQRS: Command model (yozish) | Query model (o'qish)
yozish — qoidalar | o'qish — tezlikCQRS (Command Query Responsibility Segregation) — yozish (command) va o'qish (query) uchun alohida modellar ishlatish: bir model ikkalasiga xizmat qilish o'rniga, har biri o'z vazifasiga optimallashtiriladi. Sabab: o'qish va yozish ehtiyojlari farq qiladi (yozish — qoidalar, izchillik; o'qish — tezlik, turli ko'rinish); bir model — murosaga boradi. CQRS ularni ajratadi (yozish modeli boy, o'qish modeli sodda/tez). Nom: Command (o'zgartiradi), Query (qaytaradi) — ikki mas'uliyat. CQRS — o'qish/yozish ajratish (har biri optimal).
2.2. Command va Query ajratish
Ikki xil amal:
# COMMAND — holatni o'zgartiradi (qiymat qaytarmaydi)
def pul_otkaz(hisob_id, miqdor): ... # yozish (qoida)
# QUERY — holatni qaytaradi (o'zgartirmaydi)
def balans_ol(hisob_id) -> float: ... # o'qish (tez)
# command o'zgartiradi, query o'qiydi — aralashmaydiCommand va Query: Command (buyruq) — holatni o'zgartiradi (yozish, yangilash, o'chirish — biznes qoidalari, tekshiruv), odatda qiymat qaytarmaydi (yoki faqat natija holati); Query (so'rov) — holatni qaytaradi (o'qish), holatni o'zgartirmaydi (yon ta'sirsiz). Bu tamoyil (CQS — Command Query Separation): metod yo o'zgartiradi, yo qaytaradi — ikkalasi emas. CQRS buni model darajasiga ko'taradi (alohida yozish/o'qish modellari, ba'zan alohida baza). Command — yozish (qoida), Query — o'qish (tez). Ajratish — ravshanlik va optimizatsiya.
2.3. Event Sourcing nima
Holatni hodisalar sifatida saqlash:
An'anaviy: joriy holat (balans = 100) saqlanadi
Event Sourcing: hodisalar saqlanadi
["100 kiritildi", "50 kiritildi", "50 yechildi"]
joriy holat = hodisalarni qayta o'ynash (100)Event Sourcing — tizim joriy holatini emas, unga olib kelgan hodisalar ketma-ketligini saqlash: balans "100" o'rniga hodisalar ("100 kiritildi", "50 kiritildi", "50 yechildi") saqlanadi; joriy holat hodisalarni qayta o'ynash (replay) bilan hisoblanadi. Sabab: to'liq tarix (nima, qachon, nima uchun o'zgardi — audit), o'tmishga qaytish (har vaqtdagi holat), hodisalar manba (haqiqat). An'anaviy — faqat "hozir" (o'tmish yo'qoladi); Event Sourcing — barcha o'zgarish (tarix). Event Sourcing — o'zgarishlarni saqlash (holat emas). Hodisalar — haqiqat manbai.
2.4. Holatni hodisalardan tiklash
# hodisalar ketma-ketligi
hodisalar = [
{"turi": "PulKiritildi", "miqdor": 100},
{"turi": "PulYechildi", "miqdor": 30},
]
# joriy holat = hodisalarni qayta o'ynash
balans = 0
for h in hodisalar:
if h["turi"] == "PulKiritildi": balans += h["miqdor"]
elif h["turi"] == "PulYechildi": balans -= h["miqdor"]
# balans = 70 Holatni tiklash — joriy holat hodisalarni boshidan qayta o'ynash (replay) bilan hisoblanadi: bo'sh holatdan boshlab, har hodisani qo'llash (PulKiritildi → balans oshadi). Sabab: hodisalar — haqiqat, holat — ulardan hosila (derived). Har o'qishda qayta o'ynash mumkin (yoki keshlangan holat). Bu determinizm beradi (bir xil hodisalar → bir xil holat). Yangi ko'rinish (projection) — hodisalarni boshqacha o'ynab yaratish (masalan oylik hisobot). Holatni tiklash — hodisalardan hisoblash. Hodisa manba, holat — natija.
2.5. Snapshot va optimizatsiya
Event Sourcing muammosi: ko'p hodisa bo'lsa (million tranzaksiya) — har safar boshidan qayta o'ynash sekin. Yechim: Snapshot (suratga olish) — vaqti-vaqti bilan joriy holatni saqlash (masalan har 100 hodisada), keyin faqat snapshotdan keyingi hodisalarni o'ynash. Masalan: snapshot (balans=1000, 100-hodisada) + keyingi 5 hodisa → tez hisoblash (105 hodisa emas). Bu o'qish tezligini oshiradi (to'liq tarixni saqlab). Boshqa optimizatsiya: projection (o'qish modeli — hodisalardan oldindan hisoblangan ko'rinish, CQRS bilan). Snapshot — Event Sourcing tezligi (tarixni yo'qotmasdan). Optimizatsiya — hodisa ko'p bo'lganda.
2.6. CQRS va Event Sourcing birga
CQRS va Event Sourcing ko'pincha birga ishlatiladi (lekin mustaqil): yozish tomoni Event Sourcing (command hodisa yaratadi, hodisalar saqlanadi — haqiqat manba); o'qish tomoni projection (hodisalardan o'qish modellari yaratiladi — tez, turli ko'rinish). Oqim: command → hodisa → saqlash → projection yangilanadi → query projection o'qiydi. Bu hodisaga asoslangan arxitektura 27.10-bob ning to'liq shakli: hodisalar markazda, yozish/o'qish ajratilgan. Lekin murakkab (ikki model, eventual consistency, hodisa versiyalash). CQRS/Event Sourcing birga — hodisa markazli tizim. Hodisa — yozish, projection — o'qish.
2.7. Qachon kerak (va murakkabligi)
CQRS/Event Sourcing kuchli, lekin murakkab — har loyihaga emas: kerak — murakkab domen (bank, moliya — audit muhim), yuqori o'qish/yozish nomutanosibligi (ko'p o'qish, kam yozish), to'liq tarix/audit talabi, murakkab o'qish ko'rinishlari; kerak emas — oddiy CRUD (o'qish/yozish o'xshash, tarix keraksiz — ortiqcha). Murakkabliklar: ikki model (ko'proq kod), eventual consistency (o'qish yozishdan orqada), hodisa versiyalash (sxema o'zgarishi), o'rganish egri chizig'i. Ko'p mutaxassis ogohlantiradi: CQRS/ES ni faqat haqiqatan kerak bo'lganda (KISS). CQRS/ES — murakkab domenga kuchli, oddiyga ortiqcha. Muvozanat: qiymat vs murakkablik.
2.8. CQRS/Event Sourcing — haqiqat sifatida hodisalar
CQRS/Event Sourcing chuqur g'oyasi — hodisalarni haqiqat manbai qilish: an'anaviy tizim joriy holatni haqiqat deb biladi (balans=100), o'zgarishlarni yo'qotadi; Event Sourcing o'zgarishlarni (hodisalarni) haqiqat qiladi, holat — ulardan hosila. Bu paradigma o'zgarishi: "nima bor" (holat) o'rniga "nima bo'ldi" (hodisalar). Foyda: to'liq tarix, audit, o'tmishga qaytish, yangi ko'rinishlar (hodisalarni qayta o'ynab). Bu domain events (DDD — 27.8), hodisaga asoslangan 27.10-bob bilan uyg'un. Lekin murakkablik yuqori — muvozanat kerak. CQRS/Event Sourcing — o'zgarishlarni saqlash falsafasi. Hodisalar — haqiqat, holat — hosila.
3. Tez ma'lumotnoma
# CQRS — command (yozish) va query (o'qish) ajratish
def pul_otkaz(id, miqdor): ... # Command (o'zgartiradi)
def balans_ol(id) -> float: ... # Query (qaytaradi)
# EVENT SOURCING — hodisalar saqlanadi
hodisalar = [
{"turi": "PulKiritildi", "miqdor": 100},
{"turi": "PulYechildi", "miqdor": 30},
]
# HOLATNI TIKLASH — qayta o'ynash (replay)
def holat_hisobla(hodisalar):
balans = 0
for h in hodisalar:
if h["turi"] == "PulKiritildi": balans += h["miqdor"]
elif h["turi"] == "PulYechildi": balans -= h["miqdor"]
return balans
# SNAPSHOT — vaqti-vaqti holat saqlash (tezlik)
# snapshot(balans=1000, 100-hodisa) + keyingi hodisalar
# QACHON: murakkab domen, audit, tarix (oddiy CRUD ga emas)CQRS va Event Sourcing xulosasi
CQRS — command (yozish, qoida) va query (o'qish, tez) ajratish
Event Sourcing — hodisalar saqlanadi (holat emas)
Holat = hodisalarni qayta o'ynash (replay) · snapshot (tezlik)
Birga: yozish=Event Sourcing, o'qish=projection (CQRS)
Kuchli, lekin murakkab (murakkab domen/audit uchun — KISS)
Hodisalar — haqiqat, holat — hosila4. Batafsil misollar
Misollar sof Python bilan — hodisa saqlash xotirada (deterministik).
Misol 1 — CQRS: command va query ajratish
"""CQRS: yozish (command) va o'qish (query) alohida — har biri o'z vazifasi."""
class HisobYozish:
"""Command tomoni — yozish (qoidalar bilan)."""
def __init__(self) -> None:
self._balanslar: dict = {}
def hisob_ochish(self, hisob_id: int) -> None:
self._balanslar[hisob_id] = 0
def pul_kiritish(self, hisob_id: int, miqdor: float) -> None:
# biznes qoidasi: musbat miqdor
if miqdor <= 0:
raise ValueError("Miqdor musbat bo'lishi kerak")
self._balanslar[hisob_id] += miqdor
def pul_yechish(self, hisob_id: int, miqdor: float) -> None:
# biznes qoidasi: balansdan ko'p emas
if miqdor > self._balanslar[hisob_id]:
raise ValueError("Mablag' yetarli emas")
self._balanslar[hisob_id] -= miqdor
class HisobOqish:
"""Query tomoni — o'qish (tez, qoidasiz)."""
def __init__(self, yozish: HisobYozish) -> None:
self._yozish = yozish
def balans(self, hisob_id: int) -> float:
return self._yozish._balanslar.get(hisob_id, 0)
def barcha_hisoblar(self) -> list:
return list(self._yozish._balanslar.keys())
def main() -> None:
yozish = HisobYozish()
oqish = HisobOqish(yozish)
print("=== 1. Command (yozish, qoidalar) ===")
yozish.hisob_ochish(1)
yozish.pul_kiritish(1, 100)
yozish.pul_yechish(1, 30)
print(" hisob ochildi, 100 kiritildi, 30 yechildi")
print("\n=== 2. Query (o'qish, tez) ===")
print(f" balans: {oqish.balans(1)}")
print("\n=== 3. Qoida buzilsa (command) ===")
try:
yozish.pul_yechish(1, 1000)
except ValueError as e:
print(f" {e}")
print("\n=== 4. Ajratish ===")
print(" Command: o'zgartiradi (qoida)")
print(" Query: qaytaradi (tez, o'zgartirmaydi)")
print(f" hisoblar: {oqish.barcha_hisoblar()}")
print(" ⭐ CQRS — o'qish/yozish ajratilgan")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Command (yozish, qoidalar) ===
hisob ochildi, 100 kiritildi, 30 yechildi
=== 2. Query (o'qish, tez) ===
balans: 70
=== 3. Qoida buzilsa (command) ===
Mablag' yetarli emas
=== 4. Ajratish ===
Command: o'zgartiradi (qoida)
Query: qaytaradi (tez, o'zgartirmaydi)
hisoblar: [1]
⭐ CQRS — o'qish/yozish ajratilganNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Event Sourcing: hodisalardan holat
"""Event Sourcing: hodisalar saqlanadi, holat qayta o'ynash bilan hisoblanadi."""
class HisobHodisalari:
"""Hodisalarni saqlaydi (holat emas)."""
def __init__(self) -> None:
self._hodisalar: list = []
def qosh(self, turi: str, miqdor: float) -> None:
self._hodisalar.append({"turi": turi, "miqdor": miqdor})
def hodisalar(self) -> list:
return list(self._hodisalar)
def joriy_balans(self) -> float:
# holat = hodisalarni qayta o'ynash (replay)
balans = 0.0
for h in self._hodisalar:
if h["turi"] == "PulKiritildi":
balans += h["miqdor"]
elif h["turi"] == "PulYechildi":
balans -= h["miqdor"]
return balans
def main() -> None:
hisob = HisobHodisalari()
print("=== 1. Hodisalarni saqlash ===")
hisob.qosh("PulKiritildi", 100)
hisob.qosh("PulKiritildi", 50)
hisob.qosh("PulYechildi", 30)
print(f" saqlangan hodisalar: {len(hisob.hodisalar())}")
print("\n=== 2. Holat = qayta o'ynash ===")
print(f" joriy balans: {hisob.joriy_balans()}")
print(" (100 + 50 - 30 = 120)")
print("\n=== 3. To'liq tarix ===")
for i, h in enumerate(hisob.hodisalar(), 1):
print(f" {i}. {h['turi']}: {h['miqdor']}")
print("\n=== 4. Yangi hodisa ===")
hisob.qosh("PulYechildi", 20)
print(f" yangi balans: {hisob.joriy_balans()}")
print(" hodisalar — haqiqat, balans — hosila")
print(" ⭐ Event Sourcing — hodisalardan holat")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Hodisalarni saqlash ===
saqlangan hodisalar: 3
=== 2. Holat = qayta o'ynash ===
joriy balans: 120.0
(100 + 50 - 30 = 120)
=== 3. To'liq tarix ===
1. PulKiritildi: 100
2. PulKiritildi: 50
3. PulYechildi: 30
=== 4. Yangi hodisa ===
yangi balans: 100.0
hodisalar — haqiqat, balans — hosila
⭐ Event Sourcing — hodisalardan holatNima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 3 — O'tmishga qaytish va yangi ko'rinish
"""Event Sourcing foydasi: o'tmishdagi holat va yangi ko'rinish (projection)."""
class Hisob:
def __init__(self) -> None:
self._hodisalar: list = []
def qosh(self, turi: str, miqdor: float) -> None:
self._hodisalar.append({"turi": turi, "miqdor": miqdor})
def balans_nuqtada(self, nechta_hodisa: int) -> float:
# birinchi N hodisadan keyingi holat (o'tmishga qaytish)
balans = 0.0
for h in self._hodisalar[:nechta_hodisa]:
balans += h["miqdor"] if h["turi"] == "kirim" else -h["miqdor"]
return balans
def kirimlar_yigindisi(self) -> float:
# yangi ko'rinish (projection) — faqat kirimlar
return sum(h["miqdor"] for h in self._hodisalar if h["turi"] == "kirim")
def main() -> None:
hisob = Hisob()
hisob.qosh("kirim", 100)
hisob.qosh("kirim", 50)
hisob.qosh("chiqim", 30)
hisob.qosh("kirim", 20)
print("=== 1. Joriy balans (barcha hodisa) ===")
print(f" balans: {hisob.balans_nuqtada(4)}")
print("\n=== 2. O'tmishga qaytish (2 hodisadan keyin) ===")
print(f" 2-hodisadagi balans: {hisob.balans_nuqtada(2)}")
print("\n=== 3. 3-hodisadan keyin ===")
print(f" balans: {hisob.balans_nuqtada(3)}")
print("\n=== 4. Yangi ko'rinish (projection) ===")
print(f" faqat kirimlar yig'indisi: {hisob.kirimlar_yigindisi()}")
print(" bir hodisalar — turli ko'rinishlar")
print(" ⭐ Hodisalar — o'tmish va yangi ko'rinish")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Joriy balans (barcha hodisa) ===
balans: 140.0
=== 2. O'tmishga qaytish (2 hodisadan keyin) ===
2-hodisadagi balans: 150.0
=== 3. 3-hodisadan keyin ===
balans: 120.0
=== 4. Yangi ko'rinish (projection) ===
faqat kirimlar yig'indisi: 170
bir hodisalar — turli ko'rinishlar
⭐ Hodisalar — o'tmish va yangi ko'rinishNima ko'rsatdi: 2.4, 2.6-bo'limlar.
Misol 4 — Snapshot (optimizatsiya)
"""Snapshot: vaqti-vaqti holatni saqlash — qayta o'ynashni tezlashtirish."""
class HisobSnapshot:
def __init__(self) -> None:
self._hodisalar: list = []
self._snapshot: dict = {"hodisa_orni": 0, "balans": 0.0}
def qosh(self, miqdor: float) -> None:
self._hodisalar.append(miqdor)
# har 3 hodisada snapshot (deterministik)
if len(self._hodisalar) % 3 == 0:
self._snapshot = {
"hodisa_orni": len(self._hodisalar),
"balans": self._toliq_hisobla(),
}
def _toliq_hisobla(self) -> float:
return sum(self._hodisalar)
def balans_snapshotdan(self) -> float:
# snapshotdan boshlab, faqat keyingi hodisalar (tez)
balans = self._snapshot["balans"]
for m in self._hodisalar[self._snapshot["hodisa_orni"]:]:
balans += m
return balans
def main() -> None:
hisob = HisobSnapshot()
for miqdor in [100, 50, 30, 20, 40]:
hisob.qosh(miqdor)
print("=== 1. Hodisalar ===")
print(f" jami hodisa: {len(hisob._hodisalar)}")
print("\n=== 2. Snapshot holati ===")
print(f" snapshot o'rni: {hisob._snapshot['hodisa_orni']}")
print(f" snapshot balansi: {hisob._snapshot['balans']}")
print("\n=== 3. Balans (snapshotdan) ===")
print(f" joriy balans: {hisob.balans_snapshotdan()}")
print(" snapshot (3 hodisa) + keyingi 2 (o'ynaladi)")
print("\n=== 4. Optimizatsiya ===")
print(" 5 hodisa emas, faqat 2 o'ynaldi (snapshotdan)")
print(" tarix saqlanadi (snapshot — tezlik)")
print(" ⭐ Snapshot — qayta o'ynashni tezlashtirish")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Hodisalar ===
jami hodisa: 5
=== 2. Snapshot holati ===
snapshot o'rni: 3
snapshot balansi: 180
=== 3. Balans (snapshotdan) ===
joriy balans: 240
snapshot (3 hodisa) + keyingi 2 (o'ynaladi)
=== 4. Optimizatsiya ===
5 hodisa emas, faqat 2 o'ynaldi (snapshotdan)
tarix saqlanadi (snapshot — tezlik)
⭐ Snapshot — qayta o'ynashni tezlashtirishNima ko'rsatdi: 2.5-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Bir model o'qish/yozish" | CQRS — ajratilgan |
| "Command qiymat qaytaradi" | O'zgartiradi (query qaytaradi) |
| "Faqat joriy holat" | Event Sourcing — hodisalar |
| "Hodisa holatni almashtiradi" | Hodisadan holat hosila |
| "Snapshot tarixni o'chiradi" | Tezlik (tarix saqlanadi) |
| "CQRS = Event Sourcing" | Mustaqil (ko'pincha birga) |
| "Har loyihaga" | Murakkab domen (KISS) |
| "Eventual = darhol" | O'qish yozishdan orqada |
6. Keng tarqalgan xatolar va yechimlari
1. Command qiymat qaytaradi (CQS buzildi)
def pul_otkaz(...) -> Hisob: # o'zgartir+qaytar # ⚠️
def pul_otkaz(...) -> None # faqat o'zgartir # ✅2. Query holatni o'zgartiradi
def balans_ol(id): self.log() # yon ta'sir # ⚠️
def balans_ol(id) -> float # faqat qaytar # ✅3. Hodisa nomi buyruq kabi
{"turi": "PulniYech"} # buyruq # ⚠️
{"turi": "PulYechildi"} # o'tgan fakt # ✅4. Snapshotsiz (ko'p hodisa — sekin)
# million hodisani har safar o'ynash # ⚠️
# snapshot (vaqti-vaqti holat saqlash) # ✅5. Oddiy CRUD ga Event Sourcing
# oddiy jadval CRUD ga hodisa manba # ⚠️
# oddiy — an'anaviy holat (KISS) # ✅6. Hodisa versiyalashni unutish
# hodisa sxemasi o'zgarsa — eski hodisa? # ⚠️
# versiya (v1, v2), migratsiya # ✅7. Eventual consistency ni sinxron kutish
# yozgach darhol projection o'qish kutish # ⚠️
# eventual (projection biroz orqada) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 27.10-dars (o'tilgan): Hodisaga asoslangan — hodisalar manba
- 27.8-dars (o'tilgan): DDD — domain events, aggregate
- 27.5-dars (o'tilgan): Command naqshi
- 23-qism (o'tilgan): Baza — hodisa saqlash
- 29-qism: Kafka — hodisa oqimi (event store)
8. Eng yaxshi amaliyotlar
Command o'zgartiradi, Query qaytaradi (CQS).
Hodisa — o'tgan fakt (o'tgan zamon).
Holat = hodisalardan (replay).
Snapshot (ko'p hodisa — tezlik).
Hodisa versiyalash (sxema o'zgarishi).
Eventual consistency (projection orqada).
CQRS/ES — murakkab domenga (KISS — oddiyga an'anaviy).
Hodisalar — haqiqat manba (holat — hosila).
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # CQRS nima?
2. # command nima?
3. # query nima?
4. # CQS tamoyili?
5. # Event Sourcing nima?
6. # nima saqlanadi?
7. # holat qanday hisoblanadi?
8. # replay nima?
9. # snapshot nima?
10. # snapshot nega?
11. # CQRS = Event Sourcing?
12. # qachon kerak?Javoblar
- O'qish va yozish modellarini ajratish
- Holatni o'zgartiradi (yozish)
- Holatni qaytaradi (o'qish)
- Metod yo o'zgartiradi, yo qaytaradi
- Holatni hodisalar sifatida saqlash
- Hodisalar (holat emas)
- Hodisalarni qayta o'ynash (replay)
- Hodisalarni boshidan qo'llash
- Vaqti-vaqti holatni saqlash
- Qayta o'ynashni tezlashtirish
- Mustaqil (ko'pincha birga)
- Murakkab domen, audit, tarix
Vazifa 2: Xatolarni tuzating
1. def pul_otkaz(...) -> Hisob # None
2. def balans_ol(id): self.log() # yon ta'sirsiz
3. {"turi": "PulniYech"} # o'tgan fakt
4. # million hodisani o'ynash # snapshot
5. # oddiy CRUD ga ES # an'anaviyJavoblar
1. def pul_otkaz(...) -> None
2. def balans_ol(id) -> float
3. {"turi": "PulYechildi"}
4. snapshot (vaqti-vaqti holat)
5. oddiy — an'anaviy holatVazifa 3: CQRS
Tuzing:
- Command model
- Query model
- Qoida (command)
- Tezlik (query)
Vazifa 4: Event Sourcing
Tuzing:
- Hodisalar
- Saqlash
- Replay
- Holat
Vazifa 5: O'tmish
Tuzing:
- Hodisalar
- N-nuqtada
- Yangi ko'rinish
- Projection
Vazifa 6: Snapshot
Tuzing:
- Hodisalar
- Snapshot
- Keyingilar
- Tezlik
Vazifa 7: O'ylash
Event Sourcing paradigma o'zgarishini talab qiladi: "nima bor" (joriy holat) o'rniga "nima bo'ldi" (hodisalar ketma-ketligi). Nima uchun bu o'zgarish kuchli (holat "hosila" bo'lganda ko'p imkoniyat ochiladi), va nega ko'p tizim baribir an'anaviy (joriy holat) yondashuvni tanlaydi — Event Sourcing "kuchli, lekin qimmat" ekanini qanday tushunish kerak?
Javob
Qisqa javob: "Nima bo'ldi" (hodisalar) "nima bor" (holat) dan kuchli, chunki: holat hosila bo'lsa (hodisalardan hisoblanadi), ko'p imkoniyat ochiladi — to'liq tarix (har o'zgarish saqlangan — audit, "nima uchun balans 100?"), o'tmishga qaytish (har vaqtdagi holat — "kecha balans qancha edi?"), yangi ko'rinishlar (hodisalarni boshqacha o'ynab — yangi hisobot, keyin o'ylab topilgan), debug/tahlil (hodisalar ketma-ketligini kuzatish), vaqt bo'yicha tahlil (o'zgarish naqshlari). An'anaviy tizim faqat "hozir" ni biladi (o'tmish yo'qoladi — "balans 100, lekin qanday?"). Bu kuchli, chunki ma'lumot yo'qolmaydi (har o'zgarish — qimmatli fakt). Lekin ko'p tizim an'anaviyni tanlaydi, chunki Event Sourcing qimmat: murakkablik (hodisa dizayni, replay, snapshot, versiyalash — ko'p kod, o'ylash); o'rganish (jamoa yangi paradigmani o'rganishi kerak); eventual consistency (o'qish yozishdan orqada — ba'zi holatda muammo); ortiqcha (oddiy CRUD ga — tarix keraksiz bo'lsa, faqat murakkablik). "Kuchli, lekin qimmat" — bu savdo: Event Sourcing ko'p imkoniyat beradi (tarix, audit, moslashuvchanlik), lekin murakkablik va o'rganish narxi bilan; agar bu imkoniyatlar kerak bo'lsa (bank — audit majburiy, murakkab domen) — qimmat oqlanadi; agar kerak emas (oddiy blog, CRUD) — ortiqcha (KISS buziladi). To'g'ri qaror: domen ehtiyojini baholash (tarix/audit muhimmi?) — kerak bo'lsa Event Sourcing, aks holda an'anaviy. Muhandislik saboqlari: paradigma o'zgarishi kuchli, lekin bepul emas (o'rganish, murakkablik); "nima bo'ldi" > "nima bor" faqat tarix qimmat bo'lganda; har kuchli vosita — narx bilan (imkoniyat vs murakkablik); ko'pincha oddiy (an'anaviy) yetarli — murakkabni faqat kerak bo'lganda.
1. Nega "nima bo'ldi" kuchli
Holat hosila → imkoniyatlar: tarix (audit), o'tmish (har vaqt), yangi ko'rinish (replay), tahlil. Ma'lumot yo'qolmaydi.
2. Nega an'anaviy tanlanadi
- Murakkablik (dizayn, replay, snapshot, versiya)
- O'rganish (yangi paradigma)
- Eventual consistency (orqada)
- Ortiqcha (oddiy CRUD ga)
3. "Kuchli, lekin qimmat" savdosi
| Ehtiyoj | Tanlov |
|---|---|
| Tarix/audit muhim (bank) | Event Sourcing (qimmat oqlanadi) |
| Oddiy CRUD (tarix keraksiz) | An'anaviy (KISS) |
4. To'g'ri qaror
Domen ehtiyojini baholash: tarix/audit muhimmi? Kerak bo'lsa ES, aks holda an'anaviy.
5. Muhandislik saboqlari
- Paradigma kuchli, lekin bepul emas
- "Nima bo'ldi" > "nima bor" — tarix qimmat bo'lganda
- Har vosita — narx bilan (imkoniyat vs murakkablik)
- Oddiy yetarli — murakkab faqat kerak bo'lganda
6. Xulosa
- Hodisalar — ko'p imkoniyat (tarix, audit, replay)
- An'anaviy — soddaligi uchun (ko'p tizim)
- Kuchli, lekin qimmat (savdo)
- Domen ehtiyojiga qarab (KISS)
Nimani mustahkamlaydi: 2.7, 2.8-bo'limlar.
Xulosa
Bu darsda CQRS va Event Sourcing ni o'rgandik.
Eng muhim uch fikr:
CQRS va Command/Query ajratish. CQRS (Command Query Responsibility Segregation) — yozish (command) va o'qish (query) uchun alohida modellar: bir model ikkalasiga xizmat qilish o'rniga har biri optimallashtiriladi (yozish — qoidalar/izchillik, o'qish — tezlik/ko'rinish). Command — holatni o'zgartiradi (qiymat qaytarmaydi), Query — holatni qaytaradi (o'zgartirmaydi, yon ta'sirsiz) — CQS tamoyili (metod yo o'zgartiradi, yo qaytaradi). CQRS buni model darajasiga ko'taradi (alohida yozish/o'qish, ba'zan alohida baza).
Event Sourcing va holatni tiklash. Event Sourcing — joriy holatni emas, unga olib kelgan hodisalar ketma-ketligini saqlash: balans "100" o'rniga hodisalar ("100 kiritildi", "50 kiritildi", "50 yechildi"); joriy holat hodisalarni qayta o'ynash (replay — bo'sh holatdan har hodisani qo'llash) bilan hisoblanadi. Foyda: to'liq tarix (audit — nima/qachon/nima uchun), o'tmishga qaytish, yangi ko'rinishlar (projection — hodisalarni boshqacha o'ynab). Snapshot (vaqti-vaqti holatni saqlash) — ko'p hodisada replay ni tezlashtiradi (tarixni yo'qotmasdan). Hodisalar — haqiqat, holat — hosila.
Birga, qachon, haqiqat sifatida hodisalar. CQRS va Event Sourcing ko'pincha birga (lekin mustaqil): yozish — Event Sourcing (command → hodisa → saqlash), o'qish — projection (hodisalardan o'qish modellari); hodisaga asoslangan arxitektura 27.10-bob ning to'liq shakli. Kuchli, lekin murakkab — murakkab domen (bank, moliya — audit), yuqori o'qish/yozish nomutanosibligi, tarix talabi uchun; oddiy CRUD ga ortiqcha (KISS). Murakkabliklar: ikki model, eventual consistency, hodisa versiyalash, o'rganish. Chuqur g'oya — hodisalarni haqiqat manbai qilish ("nima bor" holat o'rniga "nima bo'ldi" hodisalar); paradigma kuchli, lekin qimmat (imkoniyat vs murakkablik — savdo). Domain events (DDD 27.8), hodisaga asoslangan 27.10-bob bilan uyg'un. Hodisalar — haqiqat, holat — hosila.
Keyingi darsda xatolarga chidamlilik naqshlarini o'rganamiz: taqsimlangan tizimlarda xatolar muqarrar — Retry, Circuit Breaker, Timeout, Bulkhead, Fallback kabi naqshlar bilan tizimni xatolarga bardoshli (resilient) qilish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!