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
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 loyihaDDD — 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)
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 atamalarUbiquitous 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
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 qilishStrategik 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)
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)
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 │
└──────────────────────┴─────────────────────────────────────────────────┘// 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)
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 ishlatSubdomain — 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)
// 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)Entity — o'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)
// 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 Object — identity 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)
// 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) ichidaBuyurtmaItem'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,Mijozobyekti 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)
// 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)
// 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 reaksiyaDomain 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)
// 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 serviceDomain 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)
// 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)
// 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;
}
}Factory — murakkab 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. Konstruktorprivateqilinadi, 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)
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)
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 communicationNestJS'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)
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'rinadiEvent 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)
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)
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)
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
// 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)
// 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)
// 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)
// 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)
// 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)
// 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 serviceMisol 6 — Repository (aggregate uchun — 2.9)
// 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)
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 couplingMisol 8 — NestJS modul tuzilishi (DDD — 2.13)
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)
// 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)
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
entity faqat getter/setter, mantiq service'da (2.14)
rich model (biznes mantiq entity'da — 2.6)2) Ibtidoiy obsessiya (primitive obsession)
pul = number, email = string (validatsiya tarqoq)
Value Object (Pul, Email — validatsiya ichida — 2.7)3) Aggregate ichki entity'ga to'g'ridan kirish
buyurtma.items.push() (invariant buzilishi — 2.8)
buyurtma.itemQoshish() (root orqali)4) Oddiy CRUD'ga DDD
oddiy ma'lumotga aggregate/VO (over-engineering — 2.1)
murakkab domen'ga DDD (balans — 2.14)5) Texnik til (ubiquitous language yo'q)
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)
- Value Object: Pul, Email (immutable, validatsiya — Misol 1, 2.7).
- Entity: Mijoz (rich model, biznes xulqi — Misol 2, 2.6).
- Aggregate: Buyurtma root (invariant himoyasi — Misol 3, 2.8).
- Domain Event: BuyurtmaYaratildi/Tasdiqlandi (Misol 4, 2.10).
- Domain Service: Narxlash (Misol 5, 2.11).
- Repository: aggregate root uchun (Misol 6, 2.9).
- Use-case: event publish (Misol 4, 9.3).
- Bounded Context: modul ajratish (Misol 7, 2.4).
- Ubiquitous Language: biznes atamalari 2.2-bob.
- 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!