IlmHamroh
Python kursi/Arxitektura11/12-dars18 daqiqa
Mundarija (22)

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 — tezlik

CQRS (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:

python
# 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 — aralashmaydi

Command 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

python
# 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

python
# 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 — hosila

4. Batafsil misollar

Misollar sof Python bilan — hodisa saqlash xotirada (deterministik).

Misol 1 — CQRS: command va query ajratish

python
"""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:

text
=== 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 ajratilgan

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Event Sourcing: hodisalardan holat

python
"""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:

text
=== 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 holat

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 3 — O'tmishga qaytish va yangi ko'rinish

python
"""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:

text
=== 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'rinish

Nima ko'rsatdi: 2.4, 2.6-bo'limlar.

Misol 4 — Snapshot (optimizatsiya)

python
"""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:

text
=== 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 tezlashtirish

Nima 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)

python
def pul_otkaz(...) -> Hisob:  # o'zgartir+qaytar  # ⚠️
def pul_otkaz(...) -> None    # faqat o'zgartir    # ✅

2. Query holatni o'zgartiradi

python
def balans_ol(id): self.log()  # yon ta'sir      # ⚠️
def balans_ol(id) -> float  # faqat qaytar        # ✅

3. Hodisa nomi buyruq kabi

python
{"turi": "PulniYech"}   # buyruq                  # ⚠️
{"turi": "PulYechildi"}  # o'tgan fakt            # ✅

4. Snapshotsiz (ko'p hodisa — sekin)

python
# million hodisani har safar o'ynash             # ⚠️
# snapshot (vaqti-vaqti holat saqlash)            # ✅

5. Oddiy CRUD ga Event Sourcing

python
# oddiy jadval CRUD ga hodisa manba              # ⚠️
# oddiy — an'anaviy holat (KISS)                  # ✅

6. Hodisa versiyalashni unutish

python
# hodisa sxemasi o'zgarsa — eski hodisa?         # ⚠️
# versiya (v1, v2), migratsiya                    # ✅

7. Eventual consistency ni sinxron kutish

python
# 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

  1. Command o'zgartiradi, Query qaytaradi (CQS).

  2. Hodisa — o'tgan fakt (o'tgan zamon).

  3. Holat = hodisalardan (replay).

  4. Snapshot (ko'p hodisa — tezlik).

  5. Hodisa versiyalash (sxema o'zgarishi).

  6. Eventual consistency (projection orqada).

  7. CQRS/ES — murakkab domenga (KISS — oddiyga an'anaviy).

  8. Hodisalar — haqiqat manba (holat — hosila).


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
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
  1. O'qish va yozish modellarini ajratish
  2. Holatni o'zgartiradi (yozish)
  3. Holatni qaytaradi (o'qish)
  4. Metod yo o'zgartiradi, yo qaytaradi
  5. Holatni hodisalar sifatida saqlash
  6. Hodisalar (holat emas)
  7. Hodisalarni qayta o'ynash (replay)
  8. Hodisalarni boshidan qo'llash
  9. Vaqti-vaqti holatni saqlash
  10. Qayta o'ynashni tezlashtirish
  11. Mustaqil (ko'pincha birga)
  12. Murakkab domen, audit, tarix

Vazifa 2: Xatolarni tuzating

python
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'anaviy
Javoblar
python
1.  def pul_otkaz(...) -> None

2.  def balans_ol(id) -> float

3.  {"turi": "PulYechildi"}

4.  snapshot (vaqti-vaqti holat)

5.  oddiy — an'anaviy holat

Vazifa 3: CQRS

Tuzing:

  1. Command model
  2. Query model
  3. Qoida (command)
  4. Tezlik (query)

Vazifa 4: Event Sourcing

Tuzing:

  1. Hodisalar
  2. Saqlash
  3. Replay
  4. Holat

Vazifa 5: O'tmish

Tuzing:

  1. Hodisalar
  2. N-nuqtada
  3. Yangi ko'rinish
  4. Projection

Vazifa 6: Snapshot

Tuzing:

  1. Hodisalar
  2. Snapshot
  3. Keyingilar
  4. 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

  1. Paradigma kuchli, lekin bepul emas
  2. "Nima bo'ldi" > "nima bor" — tarix qimmat bo'lganda
  3. Har vosita — narx bilan (imkoniyat vs murakkablik)
  4. Oddiy yetarli — murakkab faqat kerak bo'lganda

6. Xulosa

  1. Hodisalar — ko'p imkoniyat (tarix, audit, replay)
  2. An'anaviy — soddaligi uchun (ko'p tizim)
  3. Kuchli, lekin qimmat (savdo)
  4. 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:

  1. 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).

  2. 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.

  3. 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.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
27.11-dars: CQRS va Event Sourcing — IlmHamroh