WisarWisar
Dasturlash kitobi/9-QISM — Arxitektura30 daqiqa

9.4-bob: Domain-Driven Design (DDD) asoslari

9-QISM — Arxitektura va ilg'or backend · 4-mavzu


1. Kirish va motivatsiya

Clean Architecture 9.3-bob bilan ilovani qatlamlarga ajratdik — biznes mantiq markazda. Endi shu markaziy qatlamni — domen (biznes soha)ni — qanday chuqur modellashtirish ni o'rganamiz: Domain-Driven Design (DDD). DDD — Eric Evans (2003) yaratgan yondashuv, murakkab biznes sohani (bank, sug'urta, logistika, e-commerce) dasturda to'g'ri aks ettirish uchun. Texnika emas, balki fikrlash usuli: kodni biznes tilida, biznes qoidalari atrofida qurish.

DDD'ning markaziy g'oyasi — kod biznesni aks ettirishi kerak. Ko'p loyihada kod texnik atamalar bilan to'la (UserManager, DataProcessor, updateRecord) — biznes egasi tushunmaydi. DDD — ubiquitous language (umumiy til): dasturchi ham, biznes egasi ham bir xil atamalar ishlatadi (Buyurtma, Yetkazib berish, Chegirma qo'llash). Kod biznes hujjati bo'ladi. Bu — murakkab domenda (oddiy CRUD emas, ko'p qoida) juda qimmatli.

Avval muhim: DDD — har loyihaga emas. Oddiy CRUD (ma'lumot saqlash/o'qish) uchun DDD ortiqcha (over-engineering). DDD — murakkab biznes mantiq (ko'p qoida, hisoblash, holat) bo'lganda. Bu bob: DDD nima/qachon, strategik (bounded context, ubiquitous language) va taktik (entity, value object, aggregate, repository, domain event) DDD, va NestJS'da amaliy. Bu bob 9.3 (Clean — domen qatlami), 9.1 (SOLID) bilan. DDD — senior/arxitektor bilimi.

O'xshatish: DDD — arxitektor va buyurtmachi bir tilda gaplashishi. Uy quryapsiz: yomon yondashuv — arxitektor texnik chizmalar bilan gapiradi (buyurtmachi tushunmaydi, noto'g'ri uy chiqadi). DDD yondashuvi — ikkalasi bir til (ubiquitous language): "mehmonxona", "oshxona", "ayvon" (biznes atamalari). Buyurtmachi (biznes egasi) chizmani tushunadi, xatoni darrov ko'radi. Uy (kod) aynan kerakli bo'ladi. Bundan tashqari, uy mantiqiy bo'limlarga (bounded context — yotoq zonasi, mehmon zonasi) ajratiladi — har biri o'z qoidasi bilan. DDD — murakkab binoni (biznesni) to'g'ri qurish san'ati.

Nega muhim?

  • Murakkab biznes — ko'p qoidani to'g'ri modellashtirish.
  • Umumiy til — kod biznesni aks ettiradi (biznes egasi tushunadi).
  • Clean bilan — domen qatlamini chuqurlashtiradi 9.3-bob.
  • Arxitektor bilim — katta tizim dizayni (senior+).

2. Nazariya — chuqur tushuntirish

2.1. DDD nima va qachon

text
  DDD — murakkab domenni dasturda to'g'ri modellashtirish (Eric Evans 2003)
   Texnika emas, FIKRLASH usuli (biznes atrofida kod)

  QACHON DDD:
   Murakkab biznes mantiq (ko'p qoida, hisoblash, holat) — bank, sug'urta, logistika
   Uzoq yashaydigan, o'sadigan loyiha
   Biznes ekspertlari bilan yaqin ishlash

  QACHON EMAS:
   Oddiy CRUD (ma'lumot saqlash/o'qish — DDD ortiqcha)
   Kichik/qisqa muddatli loyiha

DDD — murakkab domenni to'g'ri modellashtirish (fikrlash usuli — texnika emas). Qachon: murakkab biznes (ko'p qoida — bank, logistika), uzoq loyiha, biznes ekspert bilan. Qachon emas: oddiy CRUD (over-engineering — 9.3: 2.13). DDD — murakkablikni boshqarish vositasi (oddiy joyga keraksiz). Pragmatik tanlov muhim.

2.2. Ubiquitous Language (umumiy til)

text
  UBIQUITOUS LANGUAGE — dasturchi + biznes egasi BIR XIL atamalar

   Texnik til (biznes tushunmaydi):
  UserManager.processData(), updateRecord(), DataProcessor

   Biznes tili (kod = biznes):
  Buyurtma.chegirmaQolla(), Mijoz.sodiqlikDarajasi(), YetkazibBerish.rejalashtir()

   Kod biznes hujjati bo'ladi (biznes egasi o'qiy oladi)
   Suhbat, kod, hujjat — bir xil atamalar

Ubiquitous Language (DDD asosi) — dasturchi va biznes egasi bir xil atamalar (kod biznesni aks ettiradi). Texnik atama (processData) o'rniga biznes atamasi (chegirmaQolla). Kod — biznes hujjati (egasi o'qiy oladi, xatoni ko'radi). Suhbat, kod, hujjat bir til. Bu — DDD'ning eng muhim, lekin ko'p e'tibordan chetda qoladigan qismi (eng katta foyda).

2.3. Strategik vs Taktik DDD

text
  STRATEGIK DDD (katta rasm — domenni bo'lish):
  - Bounded Context (chegaralangan kontekst — 2.4)
  - Ubiquitous Language 2.2-bob
  - Context Map (kontekstlar aloqasi)
  - Subdomain (core/supporting/generic)

  TAKTIK DDD (kod naqshlari — domenni modellash):
  - Entity 2.6-bob, Value Object 2.7-bob
  - Aggregate + Aggregate Root 2.8-bob, Repository 2.9-bob
  - Domain Event 2.10-bob, Domain Service 2.11-bob
  - Application Service (2.11a), Factory (2.11b)

  Strategik'da Context Map (2.4a) — kontekstlar aloqasi;
  Event Storming (2.13a) — modelni birga kashf qilish

Strategik DDD — katta rasm (domenni qanday bo'lish — bounded context, ubiquitous language, subdomain). Taktik DDD — kod naqshlari (entity, value object, aggregate, repository — domenni kodda modellash). Strategik — muhimroq (noto'g'ri bo'linish — hammasi buziladi); taktik — amaliy qurilish bloklari. Ikkalasi birga. Boshlovchilar taktikaga e'tibor beradi, lekin strategik muhimroq.

2.4. Bounded Context (chegaralangan kontekst)

text
  BOUNDED CONTEXT — domen modelining aniq chegarasi (o'z tili, qoidasi)

  Misol — "Mahsulot" turli kontekstda har xil:
  - Katalog konteksti: nom, tavsif, rasm, narx (ko'rsatish uchun)
  - Ombor konteksti: zaxira, joylashuv, og'irlik (saqlash uchun)
  - Buyurtma konteksti: narx, miqdor (sotib olish uchun)

   Bir "Mahsulot" yo'q — har kontekstda boshqa model (chegara)
   NestJS'da: har bounded context — alohida modul (8.1)

Bounded Context — domen modelining aniq chegarasi (o'z tili, qoidasi). Eng muhim strategik tushuncha: bir tushuncha (Mahsulot) turli kontekstda har xil (katalogda — ko'rsatish; omborda — zaxira; buyurtmada — narx). Bitta ulkan model o'rniga — chegaralangan kontekstlar (har biri o'z modeli). NestJS'da har bounded context — alohida modul 8.1-bob yoki mikroservis 8.16-bob. Murakkablikni bo'lib boshqaradi.

2.4a. Context Map (kontekstlar aloqasi)

text
  CONTEXT MAP — bounded kontekstlar bir-biriga QANDAY bog'langani (jamoa + kod aloqasi)

   (yuqori oqim / upstream)  — model boshqasiga TA'SIR qiladi
   (quyi oqim / downstream)  — model boshqadan model OLADI

  Asosiy naqshlar:
  ┌──────────────────────┬─────────────────────────────────────────────────┐
  │ Partnership          │ ikki jamoa TENG — birga muvaffaqiyat/mag'lubiyat │
  │ Shared Kernel        │ umumiy KICHIK kod/model (birga egalik — ehtiyot) │
  │ Customer–Supplier    │ upstream (supplier) downstream (customer) ehtiyoji│
  │                      │ bilan HISOBLASHADI (rejalashtiradi)              │
  │ Conformist           │ downstream upstream modeliga BO'YSUNADI (ta'sir  │
  │                      │ yo'q — masalan tashqi API)                       │
  │ Anti-Corruption Layer│ downstream tarjimon qatlam bilan o'zini HIMOYA   │
  │  (ACL)               │ qiladi (tashqi model ichkariga kirmaydi)         │
  │ Open Host Service    │ upstream ochiq, hujjatlashtirilgan PROTOKOL/API  │
  │  (OHS)               │ beradi (ko'p iste'molchi uchun)                  │
  │ Published Language   │ umumiy, kelishilgan FORMAT (JSON schema, event)  │
  │                      │ — OHS ko'pincha shu til bilan gaplashadi         │
  └──────────────────────┴─────────────────────────────────────────────────┘
typescript
// ACL — tashqi to'lov provayderi modelini domenga kiritmaslik (himoya qatlami)
// Tashqi API xom javobi (o'zga model — bizniki bilan aloqasi yo'q)
interface StripeChargeResponse { id: string; amount_cents: number; currency: string; paid: boolean; }

// Anti-Corruption Layer — tashqi modelni BIZNING domen tiliga tarjima qiladi
export class TolovProvayderACL {
  constructor(private stripe: StripeClient) {}
  async tolovOl(buyurtmaId: string, summa: Pul): Promise<TolovNatijasi> {
    // tashqi (published language)  domen tili tarjimasi
    const javob: StripeChargeResponse = await this.stripe.charge({
      amount_cents: summa.miqdor * 100,
      currency: summa.valyuta.toLowerCase(),
    });
    // xom modelni domen value object'iga aylantirish (ichkariga "korrupsiya" o'tmaydi)
    return new TolovNatijasi(javob.id, javob.paid, new Pul(javob.amount_cents / 100, summa.valyuta));
  }
}
//  ACL yo'q bo'lsa: Stripe modeli butun domenga tarqaydi (provayder o'zgarsa — hamma joy buziladi)

Context Map — bounded kontekstlar bir-biriga qanday bog'langani (kod aloqasi + jamoa aloqasi). Naqshlar: Partnership (ikki jamoa teng, birga taqdir); Shared Kernel (umumiy kichik model — ehtiyotkorlik, birga egalik); Customer–Supplier (upstream downstream ehtiyoji bilan hisoblashadi); Conformist (downstream upstream'ga so'zsiz bo'ysunadi — tashqi API); Anti-Corruption Layer / ACL (tarjimon qatlam — tashqi modelni domenga kiritmaslik, eng muhim himoya); Open Host Service / OHS (ochiq protokol — ko'p iste'molchiga); Published Language (kelishilgan format — JSON schema, event). ACL amaliyotda eng ko'p kerak: eski tizim yoki tashqi API bilan integratsiyada domenni "iflos" modeldan himoya qiladi. Mikroservislarda 8.16-bob context map — servis chegaralarini rejalashtirish quroli.

2.5. Subdomain (core/supporting/generic)

text
  SUBDOMAIN turlari (biznes ahamiyatiga qarab):
  - Core domain: biznesning YURAGI (raqobat ustunligi — eng ko'p kuch)
     e-commerce: tavsiya tizimi, narxlash
  - Supporting: kerakli, lekin asosiy emas (o'rtacha kuch)
     buyurtma boshqaruvi
  - Generic: umumiy (tayyor yechim — kam kuch)
     auth, email, to'lov (tayyor servis)

   Core'ga eng ko'p e'tibor (DDD); generic'ni sotib ol/tayyor ishlat

Subdomain — domenni biznes ahamiyatiga ajratish: core (biznes yuragi — raqobat ustunligi — eng ko'p kuch), supporting (kerakli, ikkilamchi), generic (umumiy — auth/email — tayyor yechim). DDD'ni core domenga qo'lla (eng muhim); generic'ni tayyor ishlat (auth — 8.9, email — 8.10). Kuchni to'g'ri taqsimlash (hamma joyga DDD emas — core'ga).

2.6. Entity (o'ziga xoslik — identity)

typescript
// Entity — IDENTITY (o'ziga xoslik) bor, hayot sikli bor
class Mijoz {
  constructor(
    public readonly id: string,                       // IDENTITY (asosiy belgi)
    private ism: string,
    private sodiqlikBallari: number = 0,
  ) {}

  // Behavior-oriented (biznes xulqi, holat emas)
  ballQoshish(ball: number): void {
    if (ball < 0) throw new Error("Ball manfiy bo'lmaydi");
    this.sodiqlikBallari += ball;
  }
  sodiqDarajasi(): string {
    return this.sodiqlikBallari > 1000 ? "VIP" : "oddiy";   // biznes qoida
  }
}
// Ikki Mijoz — bir xil ism, lekin har xil ID  BOSHQA mijoz (identity)

Entityo'ziga xoslik (identity — ID) bor obyekt (hayot sikli davomida o'zgaradi, lekin bir xil qoladi). Ikki mijoz bir xil ismli bo'lsa ham — boshqa (ID farqli). Entity behavior-oriented (biznes xulqi metodlari — ballQoshish — holat oshkor qilish emas). Biznes qoidalar entity ichida (9.3: 2.8). Mijoz, Buyurtma, Hisob — entity (ID bilan farqlanadi).

2.7. Value Object (qiymat obyekti)

typescript
// Value Object — IDENTITY YO'Q, faqat qiymat bilan aniqlanadi (o'zgarmas)
class Pul {
  constructor(
    public readonly miqdor: number,
    public readonly valyuta: string,
  ) {
    if (miqdor < 0) throw new Error("Pul manfiy bo'lmaydi");   // validatsiya
    Object.freeze(this);                              // o'zgarmas (immutable)
  }
  qoshish(boshqa: Pul): Pul {
    if (this.valyuta !== boshqa.valyuta) throw new Error("Valyuta mos emas");
    return new Pul(this.miqdor + boshqa.miqdor, this.valyuta);   // yangi obyekt
  }
  teng(boshqa: Pul): boolean {
    return this.miqdor === boshqa.miqdor && this.valyuta === boshqa.valyuta;   // qiymat tengligi
  }
}
// 100 UZS === 100 UZS (bir xil qiymat  TENG, ID yo'q)

Value Objectidentity yo'q, faqat qiymat bilan aniqlanadi (o'zgarmas — immutable). Pul (100 UZS), Manzil, Sana oralig'i, Email — value object. Tenglik qiymat bo'yicha (100 UZS === 100 UZS — ID kerak emas). O'zgarmas (Object.freeze — yangi obyekt qaytaradi). Validatsiya konstruktorda. Entity (ID) vs Value Object (qiymat) — DDD'ning asosiy farqi. Value object kodni boyitadi (ibtidoiy obsessiyadan qochish).

2.8. Aggregate (yaxlitlik — consistency boundary)

typescript
// Aggregate — bog'liq obyektlar guruhi (bitta AGGREGATE ROOT orqali)
class Buyurtma {                                      // AGGREGATE ROOT
  private items: BuyurtmaItem[] = [];                 // ichki entity'lar (faqat root orqali)
  constructor(public readonly id: string, private status: string = "yangi") {}

  // Faqat root orqali o'zgartirish (yaxlitlik — invariant himoyasi)
  itemQoshish(mahsulotId: string, narx: Pul, miqdor: number): void {
    if (this.status !== "yangi") throw new Error("Faqat yangi buyurtmaga qo'shiladi");
    if (miqdor <= 0) throw new Error("Miqdor musbat bo'lsin");
    this.items.push(new BuyurtmaItem(mahsulotId, narx, miqdor));
  }
  jami(): Pul {
    return this.items.reduce((s, i) => s.qoshish(i.summa()), new Pul(0, "UZS"));
  }
}
class BuyurtmaItem {                                  // ichki entity (root orqali boshqariladi)
  constructor(public mahsulotId: string, private narx: Pul, private miqdor: number) {}
  summa(): Pul { return new Pul(this.narx.miqdor * this.miqdor, this.narx.valyuta); }
}

Aggregate — bog'liq obyektlar guruhi, bitta aggregate root orqali boshqariladi (yaxlitlik chegarasi). Buyurtma (root) ichida BuyurtmaItem'lar — faqat root orqali o'zgaradi (itemQoshish). Invariant (biznes qoida — "yangi buyurtmagagina qo'shiladi") root tomonidan himoyalanadi. Tashqi kod ichki entity'ga to'g'ridan tegmaydi (faqat root). Bu — consistency (izchillik) chegarasi. Repository — aggregate root uchun 2.9-bob.

Aggregate dizayn qoidalari (Vaughn Vernon — "Effective Aggregate Design"):

  • Kichik aggregate saqlang. Aggregate qanchalik kichik bo'lsa, shunchalik yaxshi: bitta root + haqiqatan birga o'zgarishi shart bo'lgan minimal obyektlar. Katta aggregate (yuzlab item) — sekin yuklash, xotira, va konkurent yangilanishda lock/konflikt muammosi.
  • Tranzaksiya chegarasi = bitta aggregate. Bir tranzaksiyada faqat bitta aggregate o'zgartiring (uni to'liq izchil saqlang). Bir necha aggregate'ni bir tranzaksiyada o'zgartirish kerak bo'lsa — bu dizayn belgisi: chegaralarni qayta ko'rib chiqing yoki domain event 2.10-bob orqali oxir-oqibat izchillik (eventual consistency) ishlating.
  • Boshqa aggregate'ga ID orqali murojaat. Aggregate ichida boshqa aggregate obyektini emas, uning ID'sini saqlang (mijozId: string, Mijoz obyekti emas). Bu chegaralarni aniq ushlaydi, ortiqcha yuklashning oldini oladi. Kerak bo'lsa root ID orqali repository'dan yuklanadi.
  • Invariant chegara ichida. Har qanday biznes invariant (masalan "buyurtma jami 0 dan katta") faqat bitta aggregate ichida to'liq tekshirilishi mumkin bo'lishi kerak — chegara shunga qarab chiziladi.

2.9. Repository (DDD'da — aggregate uchun)

typescript
// Repository — AGGREGATE ROOT uchun (kolleksiya kabi — domen tilida)
interface BuyurtmaRepository {
  topById(id: string): Promise<Buyurtma | null>;     // aggregate qaytaradi
  saqla(buyurtma: Buyurtma): Promise<void>;          // butun aggregate
  mijozBuyurtmalari(mijozId: string): Promise<Buyurtma[]>;
}
//  Repository — aggregate root uchun (BuyurtmaItem uchun emas — u root orqali)
//  DB detali yashirin (9.2: 2.13, 9.3: 2.5 — port)

Repository (DDD'da) — aggregate root uchun (kolleksiya illyuziyasi — domen tilida). Butun aggregate'ni saqlaydi/oladi (Buyurtma + itemlari birga). Faqat aggregate root uchun repository (ichki entity — root orqali, alohida repo emas). DB detali yashirin (port — 9.3: 2.5). Domen entity qaytaradi (raw emas — 9.2: 2.13). Clean'da driven adapter (9.3: 2.10).

2.10. Domain Event (domen hodisasi)

typescript
// Domain Event — domenda muhim narsa sodir bo'ldi (o'tgan zamon)
class BuyurtmaYaratildi {
  constructor(
    public readonly buyurtmaId: string,
    public readonly mijozId: string,
    public readonly sana: Date,
  ) {}
}

// Aggregate event chiqaradi
class Buyurtma {
  private events: any[] = [];
  static yarat(mijozId: string): Buyurtma {
    const buyurtma = new Buyurtma(crypto.randomUUID());
    buyurtma.events.push(new BuyurtmaYaratildi(buyurtma.id, mijozId, new Date()));   // event
    return buyurtma;
  }
  eventlarniOl() { return this.events; }
}
//  event'lar publish qilinadi (Observer — 9.2: 2.10)  email/SMS/statistika reaksiya

Domain Event — domenda muhim narsa sodir bo'ldi (o'tgan zamon — BuyurtmaYaratildi, TolovQabulQilindi). Aggregate event chiqaradi, keyin publish qilinadi (Observer — 9.2: 2.10; NestJS EventEmitter — 8.16). Boshqa qism reaksiya qiladi (email/SMS — bo'shashgan bog'lanish). Event-driven 9.7-bob, CQRS, Event Sourcing asosi. Domen "nima bo'lganini" bildiradi (loose coupling).

2.11. Domain Service (domen xizmati)

typescript
// Domain Service — bir entity'ga sig'maydigan biznes mantiq
class NarxlashXizmati {                               // domen service
  // Bir necha aggregate/qoida birga (entity'ga tegishli emas)
  chegirmaHisobla(buyurtma: Buyurtma, mijoz: Mijoz, aksiya: Aksiya): Pul {
    let chegirma = 0;
    if (mijoz.sodiqDarajasi() === "VIP") chegirma += 10;   // mijoz qoidasi
    if (aksiya.faolmi()) chegirma += aksiya.foiz;          // aksiya qoidasi
    const jami = buyurtma.jami();
    return new Pul(jami.miqdor * (1 - chegirma / 100), jami.valyuta);
  }
}
//  Bu mantiq Buyurtma'ga ham, Mijoz'ga ham tegishli emas  domain service

Domain Service — bir entity/value object'ga sig'maydigan biznes mantiq (bir necha obyekt birga). Narxlash (buyurtma + mijoz + aksiya qoidalari birga) — biror entity'ga tegishli emas domain service. Application service (use-case — 9.3: 2.9) bilan farqi: domain service — sof biznes qoida; application service — orkestratsiya (oqim, port'lar). Domain service — domen qatlamida (toza).

2.11a. Application Service (use-case orkestratori)

typescript
// Application Service — bitta USE-CASE'ni orkestratsiya qiladi (biznes qoidasi EMAS)
// Vazifasi: yuklash  domen chaqirish  saqlash  event publish (oqim boshqaruvi)
@Injectable()
export class BuyurtmaTasdiqlashUseCase {
  constructor(
    @Inject("BuyurtmaRepository") private repo: BuyurtmaRepository,   // port (2.9)
    private eventEmitter: EventEmitter2,
  ) {}

  async bajar(buyurtmaId: string): Promise<void> {
    const buyurtma = await this.repo.topById(buyurtmaId);   // 1. yuklash (infra)
    if (!buyurtma) throw new Error("Buyurtma topilmadi");
    buyurtma.tasdiqla();                                     // 2. DOMEN qoidasi (aggregate ichida)
    await this.repo.saqla(buyurtma);                         // 3. saqlash (infra)
    buyurtma.eventlarniOl().forEach((e) =>                   // 4. event publish
      this.eventEmitter.emit(e.constructor.name, e));
    buyurtma.eventlarniTozala();
  }
}
//  Application service'da biznes QOIDA YO'Q (tasdiqlash qoidasi Buyurtma.tasdiqla() ichida)
//  u faqat oqimni boshqaradi (yupqa qatlam)

Application Service — bitta use-case'ni orkestratsiya qiladi (9.3: 2.9): aggregate'ni repository'dan yuklaydi, domen metodini chaqiradi, saqlaydi, event publish qiladi. Muhim farq: application service'da biznes qoida yo'q — u yupqa (thin) koordinator; qoida domen ichida (aggregate/domain service). Agar application service ichida if-larga to'la biznes mantiq paydo bo'lsa — bu domen qatlamiga tushmagan mantiq belgisi (anemic model xavfi — 2.14). Domain Service (2.11, sof biznes qoida, domen qatlami) bilan aralashtirmang: domain service nimani hisoblaydi, application service qanday oqimni boshqaradi.

2.11b. Factory (murakkab aggregate yaratish)

typescript
// Factory — murakkab aggregate/VO yaratish mantiqini bir joyga jamlaydi
// (yaratish qoidalari konstruktordan murakkabroq bo'lganda)

// 1-shakl: static factory method (aggregate ICHIDA — eng ko'p ishlatiladigan)
class Buyurtma {
  private constructor(public readonly id: string, public readonly mijozId: string) {}
  // "yarat" — nomi biznes tilida (ubiquitous language), invariant + event bir joyda
  static yarat(mijoz: Mijoz, savat: SavatItem[]): Buyurtma {
    if (savat.length === 0) throw new Error("Bo'sh savatdan buyurtma yaratilmaydi");
    const b = new Buyurtma(crypto.randomUUID(), mijoz.id);
    savat.forEach((s) => b.itemQoshish(s.mahsulotId, s.narx, s.miqdor));
    b.events.push(new BuyurtmaYaratildi(b.id, mijoz.id, new Date()));
    return b;                                          // to'liq izchil (invariant) obyekt qaytadi
  }
  private events: any[] = [];
  itemQoshish(mahsulotId: string, narx: Pul, miqdor: number): void { /* ... */ }
}

// 2-shakl: alohida Factory klass (yaratish bir necha manba/qoidaga bog'liq bo'lsa)
class BuyurtmaFactory {
  constructor(private narxlash: NarxlashXizmati) {}
  aksiyadanYarat(mijoz: Mijoz, aksiya: Aksiya, savat: SavatItem[]): Buyurtma {
    // murakkab yaratish: aksiya narxini hisoblab, aggregate quradi
    const b = Buyurtma.yarat(mijoz, savat);
    // ...aksiyaga bog'liq qo'shimcha o'rnatishlar
    return b;
  }
}

Factorymurakkab aggregate/value object yaratish mantiqini bir joyga jamlaydi (yaratish qoidalari oddiy new'dan murakkabroq bo'lganda). Ikki shakl: static factory method (aggregate ichida — Buyurtma.yarat(...), eng ko'p ishlatiladigan, nomi biznes tilida) va alohida Factory klass (yaratish bir necha manba/qoidaga bog'liq bo'lganda). Foydasi: yaratilgan aggregate darrov to'liq izchil (invariant buzilmagan) bo'ladi — yarim tayyor obyekt hech qachon chiqmaydi. Konstruktor private qilinadi, faqat factory orqali yaratiladi. GoF Factory (9.2: 2.7) bilan g'oyaviy bir xil, lekin DDD'da domen tili va invariant himoyasiga urg'u.

2.12. DDD qatlamlari (Clean bilan — 9.3)

text
  DDD + Clean 9.3-bob qatlamlari:
  ┌──────────────────────────────────────┐
  │ Presentation (controller — 9.3: 2.10) │
  ├──────────────────────────────────────┤
  │ Application (use-case — orkestratsiya) │   application service
  ├──────────────────────────────────────┤
  │ DOMAIN (entity, VO, aggregate,        │   DDD yuragi (taktik DDD)
  │   domain service, domain event, port) │
  ├──────────────────────────────────────┤
  │ Infrastructure (repo impl, DB)        │   driven adapter
  └──────────────────────────────────────┘
   DDD domen qatlamini boyitadi (Clean — struktura, DDD — domen modeli)

DDD + Clean 9.3-bob: DDD — Clean'ning domen qatlamini boyitadi (entity, value object, aggregate, domain event). Clean — struktura (qatlamlar, bog'liqlik); DDD — domen modeli (biznes qanday ifodalanadi). Ikkalasi birga — toza struktura + boy domen. Application qatlami (use-case) domen'ni orkestratsiya qiladi. Bu — murakkab biznes uchun ideal.

2.13. NestJS'da DDD (amaliy — modul = bounded context)

text
  NestJS modul 8.1-bob  bounded context 2.4-bob:
  src/
  ├── buyurtma/           Buyurtma bounded context (modul)
  │   ├── domain/             (entity, VO, aggregate, event, port — taktik DDD)
  │   ├── application/        (use-case)
  │   ├── infrastructure/     (repo impl)
  │   └── presentation/       (controller)
  ├── katalog/            Katalog bounded context (alohida modul)
  └── tolov/              To'lov bounded context

  Kontekstlar aloqasi: domain event (EventEmitter — 8.16) / module communication

NestJS'da DDD: modul 8.1-bob = bounded context 2.4-bob — har biri o'z domeni, tili. Modul ichida Clean qatlamlar (9.3: 2.12) + taktik DDD (entity, aggregate). Kontekstlararo aloqa — domain event (EventEmitter — 8.16, 2.10) yoki modul aloqasi. NestJS'ning modulli, opinionated (aniq tuzilishga yo'naltirilgan) arxitekturasi DDD'ga tabiiy mos keladi: har modul o'z chegarasi bilan yopiq, DI orqali port'lar ulanadi.

2.13a. Event Storming (domenni birga kashf qilish)

text
  EVENT STORMING — domen ekspertlari + dasturchilar birga domenni MODELLASHTIRISH
  (Alberto Brandolini). Devor + rangli stiker qog'ozlar bilan ishlanadi.

  Rangli stiker "til"i (chapdan o'ngga vaqt oqimi bo'yicha):
   Domain Event   — o'tgan zamon fakti ("BuyurtmaTasdiqlandi") — asosiy element
   Command        — event'ni keltirib chiqargan harakat ("BuyurtmaniTasdiqla")
   Aggregate      — command qabul qilib, event chiqaradigan (Buyurtma)
   Policy         — "har safar X bo'lsa, Y qil" (event  keyingi command)
   Read Model     — qaror uchun kerakli ko'rinish
   Actor          — command yuboradigan foydalanuvchi/rol
   Hotspot        — nizoli/noaniq joy (ochiq savol)

   Natija: bounded context chegaralari 2.4-bob, aggregate'lar 2.8-bob,
    domain event'lar 2.10-bob TABIIY ravishda ko'rinadi

Event Storming — domen ekspertlari va dasturchilar birga domenni kashf qiladigan workshop usuli (Alberto Brandolini). Devorga rangli stiker qog'ozlar yopishtiriladi: domain event (o'tgan zamon — asosiy element), command (event sababchisi), aggregate, policy (event keyingi command reaksiyasi), read model, hotspot (nizoli joy). Vaqt oqimi bo'yicha chapdan o'ngga terib chiqiladi. Foydasi: bounded context chegaralari 2.4-bob, aggregate'lar 2.8-bob va domain event'lar 2.10-bob qog'ozdagi guruhlanishdan tabiiy ko'rinadi — DDD modelini kodga o'tishdan oldin arzon (qog'ozda) tekshirish. Bu — ubiquitous language'ni 2.2-bob shakllantirishning eng samarali amaliy usuli.

2.13b. DDD, CQRS va Event Sourcing bog'lanishi (9.7)

text
  DDD taktik bloklar  ilg'or naqshlarga tabiiy ulanadi (9.7-bob):

  Domain Event 2.10-bob ──┬── EVENT SOURCING: holatni SAQLASH o'rniga
                        │    event'lar KETMA-KETLIGI saqlanadi
                        │    (aggregate holati event'lardan qayta tiklanadi)
                        │
                        └── CQRS: buyruq (write — aggregate, invariant) va
                             so'rov (read — optimallashtirilgan read model)
                             modelini AJRATISH

  Aggregate 2.8-bob — CQRS'da WRITE tomoni (izchillik chegarasi)
  Read Model      — CQRS'da READ tomoni (denormallashgan, tez o'qish)

DDD CQRS/Event Sourcing 9.7-bob: DDD taktik bloklar bu ilg'or naqshlarning asosi. Event Sourcing — aggregate joriy holatini saqlash o'rniga uni o'zgartirgan domain event'lar ketma-ketligini saqlash (holat event'lardan qayta hisoblanadi — to'liq tarix, audit). CQRS (Command Query Responsibility Segregation) — yozish modeli (aggregate + invariant, 2.8) va o'qish modeli (denormallashgan read model, tez) ni ajratish. Domain event 2.10-bob — ikkalasining ham ulanish nuqtasi. Bu naqshlar 9.7-bobda chuqur; bu yerda muhimi — DDD ularsiz ham to'liq ishlaydi, lekin murakkab domenda tabiiy ravishda ularga yetaklaydi. Boshidan hammasini qo'llash shart emas (over-engineering — 2.1).

2.14. DDD'ni haddan oshirmaslik (balans)

text
   DDD — murakkab domen uchun (oddiy CRUD'ga emas — 2.1):
  - Anemic model'dan qoch (entity faqat getter/setter — biznes mantiq service'da)
     DDD: biznes mantiq entity ichida (rich model — 2.6)
  - Lekin oddiy CRUD'ga DDD = over-engineering (9.3: 2.13)
  - Taktik DDD'siz strategik (ubiquitous language) ham qimmatli

  Qoida: domen murakkabligiga mos (core domen — DDD; generic — oddiy)

DDD balans: murakkab domen uchun (oddiy CRUD'ga over-engineering). Anemic model (entity faqat getter/setter, mantiq service'da) — DDD emas (rich model — biznes entity ichida — 2.6). Lekin oddiy joyga DDD ham yomon. Strategik DDD (ubiquitous language — 2.2) deyarli har joyda qimmatli (taktik'siz ham). Core domen — to'liq DDD; generic (auth) — oddiy. Pragmatizm (9.3: 2.13).

2.15. Best practices (DDD)

text
   Ubiquitous Language (kod = biznes tili — 2.2)
   Bounded Context (domenni bo'lish — modul — 2.4, 2.13)
   Core domen'ga DDD (generic — oddiy — 2.5)
   Entity (identity + biznes xulqi — rich model — 2.6)
   Value Object (qiymat, immutable — 2.7)
   Aggregate root (yaxlitlik, invariant himoyasi — 2.8)
   Repository — aggregate root uchun 2.9-bob
   Domain Event (loose coupling — 2.10, 9.7)
   Factory (murakkab aggregate — izchil yaratish — 2.11b)
   Application service yupqa (orkestratsiya, qoida yo'q — 2.11a)
   Context Map + ACL (tashqi integratsiya himoyasi — 2.4a)
   Kichik aggregate; tranzaksiya = 1 aggregate; ID orqali murojaat 2.8-bob
   Anemic model'dan qoch; oddiy CRUD'ga DDD emas (balans — 2.14)

3. Sintaksis — tez ma'lumotnoma

typescript
// Entity 2.6-bob: identity + biznes xulqi
class Mijoz { constructor(public readonly id: string) {} ballQoshish() {} }

// Value Object 2.7-bob: qiymat, immutable
class Pul { constructor(readonly miqdor: number, readonly valyuta: string) { Object.freeze(this); } }

// Aggregate 2.8-bob: root orqali
class Buyurtma { private items = []; itemQoshish() {} }   // root

// Repository 2.9-bob: aggregate root uchun
interface BuyurtmaRepository { topById(id): Promise<Buyurtma>; saqla(b: Buyurtma); }

// Domain Event 2.10-bob: o'tgan zamon
class BuyurtmaYaratildi { constructor(readonly buyurtmaId: string) {} }

4. Batafsil kod namunalari

Misol 1 — Value Object'lar (2.7)

typescript
// Pul value object
export class Pul {
  constructor(public readonly miqdor: number, public readonly valyuta: string = "UZS") {
    if (miqdor < 0) throw new Error("Pul manfiy bo'lmaydi");
    Object.freeze(this);
  }
  qoshish(b: Pul): Pul {
    this.valyutaTekshir(b);
    return new Pul(this.miqdor + b.miqdor, this.valyuta);
  }
  kopaytir(koef: number): Pul { return new Pul(this.miqdor * koef, this.valyuta); }
  teng(b: Pul): boolean { return this.miqdor === b.miqdor && this.valyuta === b.valyuta; }
  private valyutaTekshir(b: Pul) {
    if (this.valyuta !== b.valyuta) throw new Error("Valyuta mos emas");
  }
}

// Email value object (validatsiya + tur xavfsizlik)
export class Email {
  constructor(public readonly qiymat: string) {
    if (!/^[^@]+@[^@]+\.[^@]+$/.test(qiymat)) throw new Error("Email noto'g'ri");
    Object.freeze(this);
  }
}
//  ibtidoiy (string) o'rniga ma'noli tur (validatsiya ichida — primitive obsession'dan qochish)

Misol 2 — Entity (rich model — 2.6)

typescript
// Mijoz entity (identity + biznes xulqi — rich, anemic emas)
export class Mijoz {
  private constructor(
    public readonly id: string,
    private ism: string,
    private email: Email,                             // value object (Misol 1)
    private sodiqlikBallari: number = 0,
  ) {}

  static royxatdanOt(ism: string, email: string): Mijoz {
    if (ism.length < 2) throw new Error("Ism juda qisqa");   // biznes qoida
    return new Mijoz(crypto.randomUUID(), ism, new Email(email));
  }

  // Biznes xulqi (entity ichida — rich model)
  ballQoshish(ball: number): void {
    if (ball < 0) throw new Error("Ball manfiy bo'lmaydi");
    this.sodiqlikBallari += ball;
  }
  sodiqDarajasi(): "oddiy" | "kumush" | "VIP" {
    if (this.sodiqlikBallari >= 5000) return "VIP";
    if (this.sodiqlikBallari >= 1000) return "kumush";
    return "oddiy";
  }
  chegirmaFoizi(): number {
    return { oddiy: 0, kumush: 5, VIP: 10 }[this.sodiqDarajasi()];   // biznes qoida
  }
}

Misol 3 — Aggregate (Buyurtma root — 2.8)

typescript
// Buyurtma aggregate (root — yaxlitlik himoyasi)
export class Buyurtma {
  private items: BuyurtmaItem[] = [];
  private events: any[] = [];

  private constructor(
    public readonly id: string,
    public readonly mijozId: string,
    private status: "yangi" | "tasdiqlangan" | "yetkazildi" | "bekor" = "yangi",
  ) {}

  static yarat(mijozId: string): Buyurtma {
    const b = new Buyurtma(crypto.randomUUID(), mijozId);
    b.events.push(new BuyurtmaYaratildi(b.id, mijozId, new Date()));   // domain event (2.10)
    return b;
  }

  // INVARIANT himoyasi (faqat root orqali)
  itemQoshish(mahsulotId: string, narx: Pul, miqdor: number): void {
    if (this.status !== "yangi") throw new Error("Faqat yangi buyurtmaga qo'shiladi");
    if (miqdor <= 0) throw new Error("Miqdor musbat bo'lsin");
    const mavjud = this.items.find((i) => i.mahsulotId === mahsulotId);
    if (mavjud) mavjud.miqdorOshir(miqdor);
    else this.items.push(new BuyurtmaItem(mahsulotId, narx, miqdor));
  }

  tasdiqla(): void {
    if (this.items.length === 0) throw new Error("Bo'sh buyurtma tasdiqlanmaydi");
    this.status = "tasdiqlangan";
    this.events.push(new BuyurtmaTasdiqlandi(this.id));
  }

  jami(): Pul {
    return this.items.reduce((s, i) => s.qoshish(i.summa()), new Pul(0, "UZS"));
  }
  eventlarniOl() { return [...this.events]; }
  eventlarniTozala() { this.events = []; }
}

class BuyurtmaItem {                                  // ichki entity (root orqali)
  constructor(public readonly mahsulotId: string, private narx: Pul, private miqdor: number) {}
  miqdorOshir(qancha: number) { this.miqdor += qancha; }
  summa(): Pul { return this.narx.kopaytir(this.miqdor); }
}

Misol 4 — Domain Event'lar (2.10)

typescript
// Domain event'lar (o'tgan zamon — domain/events/)
export class BuyurtmaYaratildi {
  constructor(
    public readonly buyurtmaId: string,
    public readonly mijozId: string,
    public readonly sana: Date,
  ) {}
}
export class BuyurtmaTasdiqlandi {
  constructor(public readonly buyurtmaId: string) {}
}

// Use-case event'larni publish qiladi (Observer — 9.2: 2.10, NestJS — 8.16)
@Injectable()
export class CreateOrderUseCase {
  constructor(
    @Inject("BuyurtmaRepository") private repo: BuyurtmaRepository,
    private eventEmitter: EventEmitter2,             // NestJS event (8.16)
  ) {}
  async bajar(mijozId: string, items: any[]): Promise<Buyurtma> {
    const buyurtma = Buyurtma.yarat(mijozId);
    items.forEach((i) => buyurtma.itemQoshish(i.mahsulotId, new Pul(i.narx), i.miqdor));
    await this.repo.saqla(buyurtma);
    // Domain event'larni publish (loose coupling — email/SMS reaksiya qiladi)
    buyurtma.eventlarniOl().forEach((e) => this.eventEmitter.emit(e.constructor.name, e));
    buyurtma.eventlarniTozala();
    return buyurtma;
  }
}

Misol 5 — Domain Service (2.11)

typescript
// Narxlash domain service (bir necha aggregate qoidasi birga)
export class NarxlashXizmati {
  chegirmalanganNarx(buyurtma: Buyurtma, mijoz: Mijoz, aksiya?: Aksiya): Pul {
    const jami = buyurtma.jami();
    let foiz = mijoz.chegirmaFoizi();                 // mijoz qoidasi (Misol 2)
    if (aksiya?.faolmi()) foiz += aksiya.foiz;        // aksiya qoidasi
    foiz = Math.min(foiz, 30);                        // maks 30% (biznes qoida)
    return jami.kopaytir(1 - foiz / 100);
  }
}
//  Bu mantiq Buyurtma yoki Mijoz'ga yolg'iz tegishli emas  domain service

Misol 6 — Repository (aggregate uchun — 2.9)

typescript
// Port (domain qatlamida)
export interface BuyurtmaRepository {
  topById(id: string): Promise<Buyurtma | null>;
  saqla(buyurtma: Buyurtma): Promise<void>;
  mijozBuyurtmalari(mijozId: string): Promise<Buyurtma[]>;
}

// Adapter (infrastructure — butun aggregate'ni saqlaydi)
@Injectable()
export class TypeOrmBuyurtmaRepository implements BuyurtmaRepository {
  constructor(@InjectRepository(BuyurtmaOrm) private repo: Repository<BuyurtmaOrm>) {}

  async saqla(buyurtma: Buyurtma): Promise<void> {
    const orm = this.toOrm(buyurtma);                 // domen aggregate  ORM (item'lari bilan)
    await this.repo.save(orm);                        // butun aggregate (root + items)
  }
  async topById(id: string): Promise<Buyurtma | null> {
    const orm = await this.repo.findOne({ where: { id }, relations: ["items"] });   // (8.4)
    return orm ? this.toDomain(orm) : null;           // ORM  domen aggregate
  }
  async mijozBuyurtmalari(mijozId: string) { return []; }
  private toOrm(b: Buyurtma): BuyurtmaOrm { return {} as any; }
  private toDomain(orm: BuyurtmaOrm): Buyurtma { return {} as any; }
}

Misol 7 — Bounded Context (modul — 2.4, 2.13)

text
  E-commerce bounded context'lar (har biri modul — 8.1):

  ┌──────────────┐  domain event   ┌──────────────┐
  │  Katalog     │ ───────────────│  Buyurtma    │
  │  (mahsulot:  │                  │  (mahsulot:  │
  │   tavsif,    │                  │   narx,      │
  │   rasm)      │                  │   miqdor)    │
  └──────────────┘                  └──────┬───────┘
                                           │ BuyurtmaYaratildi
                                    ┌──────▼───────┐
                                    │  To'lov      │
                                    │  Omborxona   │
                                    └──────────────┘

   Har kontekstda "Mahsulot" boshqa model (katalog: tavsif; buyurtma: narx)
   Aloqa: domain event 8.16-bob — loose coupling

Misol 8 — NestJS modul tuzilishi (DDD — 2.13)

text
  src/buyurtma/                     Buyurtma bounded context
  ├── domain/
  │   ├── buyurtma.aggregate.ts        (Misol 3)
  │   ├── buyurtma-item.entity.ts
  │   ├── pul.value-object.ts          (Misol 1)
  │   ├── narxlash.service.ts          (domain service — Misol 5)
  │   ├── events/buyurtma-yaratildi.ts (Misol 4)
  │   └── buyurtma.repository.ts       (PORT — Misol 6)
  ├── application/
  │   ├── create-order.use-case.ts     (Misol 4)
  │   └── handlers/                     (event handler'lar)
  ├── infrastructure/
  │   ├── typeorm-buyurtma.repository.ts  (Misol 6)
  │   └── buyurtma.orm-entity.ts
  ├── presentation/
  │   └── buyurtma.controller.ts
  └── buyurtma.module.ts               (DI — 9.3: Misol 5)

Misol 9 — Anemic vs Rich model (2.14)

typescript
//  ANEMIC model (DDD emas — mantiq tashqarida)
class BuyurtmaAnemic {
  id: string; items: any[]; status: string;          // faqat ma'lumot (getter/setter)
}
class BuyurtmaServiceAnemic {
  itemQoshish(b: BuyurtmaAnemic, item: any) {         // mantiq service'da (tarqoq)
    if (b.status !== "yangi") throw new Error("...");
    b.items.push(item);
  }
}

//  RICH model (DDD — mantiq entity ichida)
class BuyurtmaRich {
  private items: any[] = [];
  itemQoshish(item: any) {                            // mantiq entity'da (markazlashgan)
    if (this.status !== "yangi") throw new Error("...");
    this.items.push(item);
  }
}
//  Rich: biznes qoida entity bilan birga (topish/o'zgartirish oson, izchil)

Misol 10 — DDD qaror (qachon — 2.1, 2.14)

text
  DDD kerakmi? (qaror):

  Domen murakkabmi? (ko'p biznes qoida, hisoblash, holat)
  ├─ YO'Q (oddiy CRUD)  DDD KERAK EMAS (oddiy layered — 9.3, over-engineering)
  └─ HA  davom et:
     │
     Uzoq yashaydimi? Biznes ekspert bormi?
     ├─ YO'Q  strategik DDD (ubiquitous language — 2.2) yetadi
     └─ HA  to'liq DDD (strategik + taktik):
        - Core domen'ga 2.5-bob — to'liq taktik DDD
        - Generic (auth/email) — oddiy/tayyor (2.5)

   Eng muhim: ubiquitous language (deyarli har joyda foydali — 2.2)

5. To'g'ri va noto'g'ri holatlar

1) Anemic model

text
 entity faqat getter/setter, mantiq service'da (2.14)
 rich model (biznes mantiq entity'da — 2.6)

2) Ibtidoiy obsessiya (primitive obsession)

text
 pul = number, email = string (validatsiya tarqoq)
 Value Object (Pul, Email — validatsiya ichida — 2.7)

3) Aggregate ichki entity'ga to'g'ridan kirish

text
 buyurtma.items.push() (invariant buzilishi — 2.8)
 buyurtma.itemQoshish() (root orqali)

4) Oddiy CRUD'ga DDD

text
 oddiy ma'lumotga aggregate/VO (over-engineering — 2.1)
 murakkab domen'ga DDD (balans — 2.14)

5) Texnik til (ubiquitous language yo'q)

text
 processData(), DataManager (biznes tushunmaydi — 2.2)
 chegirmaQolla(), sodiqDarajasi() (biznes tili)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — DDD'ni oddiy loyihaga

Sababi: "zamonaviy" deb (over-engineering — 2.1). Yechimi: murakkab domen'ga; oddiyga layered 9.3-bob.

Xato 2 — Anemic model

Sababi: OOP'ni to'liq ishlatmaslik 2.14-bob. Yechimi: biznes mantiq entity'ga (rich).

Xato 3 — Katta aggregate

Sababi: hamma narsa bir aggregate'da (sekin, lock). Yechimi: kichik aggregate (faqat birga o'zgaradigan).

Xato 4 — Repository har entity uchun

Sababi: aggregate tushunilmagan 2.9-bob. Yechimi: faqat aggregate root uchun.

Xato 5 — Bounded context aralash

Sababi: bir ulkan model 2.4-bob. Yechimi: kontekstlarga bo'l (modul).

Xato 6 — Value Object o'zgaruvchan

Sababi: immutable emas 2.7-bob. Yechimi: Object.freeze; yangi obyekt qaytar.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Clean Architecture 9.3-bob: DDD — domen qatlami.
  • SOLID 9.1-bob: entity/VO — SRP, rich model.
  • OOP (2.10, 7): entity, encapsulation.
  • Repository (9.2, 8.3): aggregate uchun.
  • Domain Event 9.7-bob: event-driven, CQRS, Event Sourcing (2.13b).
  • Factory (9.2: 2.7): murakkab aggregate yaratish.
  • Anti-Corruption Layer (2.4a): tashqi/eski tizim integratsiyasi.
  • Microservices 8.16-bob: bounded context — xizmat; context map — servis chegarasi.
  • NestJS modul 8.1-bob: bounded context.
  • EventEmitter 8.16-bob: domain event publish.

8. Eng yaxshi amaliyotlar (best practices)

  • Ubiquitous Language (kod = biznes tili — 2.2).
  • Bounded Context (domenni bo'lish — modul — 2.4, 2.13).
  • Core domen'ga DDD (generic — oddiy — 2.5).
  • Entity (identity + biznes xulqi — rich model — 2.6).
  • Value Object (qiymat, immutable — 2.7).
  • Aggregate root (yaxlitlik, invariant — 2.8); kichik aggregate.
  • Repository — aggregate root uchun 2.9-bob.
  • Domain Event (loose coupling — 2.10, 9.7).
  • Anemic model'dan qoch; oddiy CRUD'ga DDD emas (balans — 2.14).
  • Domain Service (entity'ga sig'maydigan mantiq — 2.11); Application Service yupqa (2.11a).
  • Factory (murakkab aggregate izchil yaratish — 2.11b).
  • Context Map + ACL (kontekstlararo aloqa, tashqi model himoyasi — 2.4a).
  • Event Storming (domenni birga kashf qilish — 2.13a).

9. Amaliy loyiha: "DDD bilan Buyurtma Domeni"

DDD'ni amalda mustahkamlash.

Maqsad

Buyurtma domenini DDD bilan modellashtirish: entity, value object, aggregate, domain event, domain service, repository.

Talablar (requirements)

  1. Value Object: Pul, Email (immutable, validatsiya — Misol 1, 2.7).
  2. Entity: Mijoz (rich model, biznes xulqi — Misol 2, 2.6).
  3. Aggregate: Buyurtma root (invariant himoyasi — Misol 3, 2.8).
  4. Domain Event: BuyurtmaYaratildi/Tasdiqlandi (Misol 4, 2.10).
  5. Domain Service: Narxlash (Misol 5, 2.11).
  6. Repository: aggregate root uchun (Misol 6, 2.9).
  7. Use-case: event publish (Misol 4, 9.3).
  8. Bounded Context: modul ajratish (Misol 7, 2.4).
  9. Ubiquitous Language: biznes atamalari 2.2-bob.
  10. Balans: rich model, anemic'dan qochish (Misol 9, 2.14).

Maslahatlar (hint)

  • VO immutable (Object.freeze — 2.7, 6-xato).
  • Rich model (mantiq entity'da — 2.6, 2-xato).
  • Aggregate root orqali (invariant — 2.8, 3-holat).
  • Repository aggregate uchun (2.9, 4-xato).
  • Ubiquitous language (biznes tili — 2.2).
  • DDD murakkab domen'ga (2.1, 1-xato).

"Tayyor" mezonlari (acceptance criteria)

  • Value Object (immutable).
  • Entity (rich).
  • Aggregate (invariant).
  • Domain Event.
  • Domain Service.
  • Repository (aggregate).
  • Use-case (event).
  • Bounded Context.
  • Ubiquitous Language.
  • Rich model (anemic emas).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda Domain-Driven Design asoslarini chuqur o'rgandik:

  • DDD nima/qachon (murakkab domen — 2.1); Ubiquitous Language (kod = biznes tili — 2.2); strategik vs taktik 2.3-bob.
  • Strategik: Bounded Context (domen chegarasi — 2.4); Context Map (partnership/shared-kernel/customer-supplier/conformist/ACL/OHS/published-language — 2.4a); subdomain (core/supporting/generic — 2.5); Event Storming (2.13a).
  • Taktik: Entity (identity + rich — 2.6); Value Object (qiymat, immutable — 2.7); Aggregate + root (yaxlitlik, invariant, kichik aggregate, tranzaksiya chegarasi — 2.8); Repository (aggregate — 2.9); Domain Event 2.10-bob; Domain Service 2.11-bob va Application Service farqi (2.11a); Factory (2.11b).
  • Clean bilan (9.3 — 2.12); NestJS (modul = context — 2.13); balans (anemic'dan qoch, oddiyga emas — 2.14).

Keyingi bob — 9.5-bob: Monolith vs Microservices — qachon qaysi. Domen modellashtirished bildik; endi arxitektura miqyosidagi eng katta qarorni — monolit yoki mikroservis — chuqur ko'ramiz (8.16'da texnik tomonini ko'rgandik; endi arxitektura qarori sifatida: trade-off, qachon o'tish, modular monolith, Conway qonuni).


Foydalanilgan rasmiy/ishonchli manbalar

  • Eric Evans — "Domain-Driven Design: Tackling Complexity in the Heart of Software" (2003) — asosiy manba (ubiquitous language, bounded context, entity, value object, aggregate, repository, factory, domain service, domain event).
  • Vaughn Vernon — "Implementing Domain-Driven Design" (2013) — amaliy qo'llanma.
  • Vaughn Vernon — "Effective Aggregate Design" (maqolalar seriyasi) — kichik aggregate, tranzaksiya chegarasi, ID orqali murojaat qoidalari.
  • Vaughn Vernon — "Domain-Driven Design Distilled" (2016) — qisqa kirish, context map naqshlari.
  • Alberto Brandolini — "Introducing EventStorming" — event storming workshop usuli.
  • Martin Fowler — "AnemicDomainModel" va "BoundedContext" maqolalari (martinfowler.com) — anti-pattern va strategik naqsh tahlili.
  • NestJS rasmiy hujjatlari — Modules, Providers (Dependency Injection), CQRS moduli.
  • TypeORM rasmiy hujjatlari — Repository, Relations (repository adapter uchun).

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
9.4-bob: Domain-Driven Design (DDD) asoslari — Wisar