Bu qatlamda nima bor
Core darslik asoslarni berdi. Bu Deep qatlam senior darajadagi chuqur mexanikani ochadi: SOLID prinsiplari chuqur, DDD taktik naqshlar (Entity/VO/Aggregate, Bounded Context, Domain Events), anemic vs rich domain model, mapping strategiyalari, screaming architecture va use case chegaralari, qatlamlar bo‘yicha test strategiyasi hamda qachon qoidalarni ongli buzish. Oxirida — butun 15 mavzuli kursning yakuni.
SOLID chuqur
SRP·OCP·LSP·ISP·DIPCore’da DIP’ni ko‘rdik — u SOLID’ning "D"si. Senior beshtasini ham chuqur biladi, chunki Clean Architecture aynan SOLID’ning tizimli qo‘llanilishi.
| Prinsip | Mohiyat |
|---|---|
| S — Single Responsibility | Sinf bitta sabab uchun o‘zgarsin (bitta "aktyor"ga xizmat qilsin) — controller, biznes, DB bir joyda emas |
| O — Open-Closed | Kengaytirishga ochiq, o‘zgartirishga yopiq — yangi xatti-harakat qo‘shishda eski kodni buzmaysan |
| L — Liskov Substitution | Bola sinf ota o‘rniga muammosiz qo‘yilsin — adapter port shartnomasini buzmasin |
| I — Interface Segregation | Kichik, fokuslangan interfeyslar — mijoz keraksiz metodlarga bog‘lanmasin |
| D — Dependency Inversion | Abstraksiyaga bog‘lan (Core’da — arxitekturaning yuragi) |
// ISP — kichik, fokuslangan interfeyslar (bitta ulkan emas)
interface OrderReader { findById(id: string): Promise<Order | null>; }
interface OrderWriter { save(order: Order): Promise<void>; }
// use case faqat KERAKLISIGA bog'lanadi -> keraksiz metodlarga bog'liq emas
// OCP — yangi to'lov usuli QO'SHISH uchun mavjud kodni O'ZGARTIRMAYSAN
interface PaymentMethod { pay(amount: Money): Promise<void>; }
class CardPayment implements PaymentMethod { /* ... */ }
class ClickPayment implements PaymentMethod { /* ... */ } // yangi -> qo'shildi, eski buzilmadi
Clean Architecture — bu prinsiplarning natijasi: DIP (portga bog‘lan) + ISP (kichik portlar) + OCP (adapter qo‘shib kengaytir) birga Ports & Adapters tuzilmasini hosil qiladi. SRP qatlamlarni ajratadi, LSP adapter’lar almashtirilishini kafolatlaydi. SOLID’ni tushunsang — arxitekturani "nega" shundayligini tushunasan.
Entity, Value Object, Aggregate
aggregate · invariantDomain qatlamini "sof" deb aytdik — lekin uni qanday modellashtirish kerak? DDD (Domain-Driven Design) taktik naqshlar aynan shu haqda.
Entity va Value Object
- Entity: o‘ziga xoslikka (identity) ega, vaqt bilan o‘zgaradi. Ikki buyurtma barcha maydonlari bir xil bo‘lsa ham — ID’si boshqa bo‘lsa, boshqa (Order, User).
- Value Object: o‘ziga xoslik yo‘q, qiymat bo‘yicha teng, o‘zgarmas (Money, Address, DateRange). 100 so‘m — har doim 100 so‘m.
// VALUE OBJECT — o'ziga xoslik yo'q, qiymat bo'yicha teng, O'ZGARMAS (immutable)
export class Money {
constructor(readonly amount: number, readonly currency: string) {
if (amount < 0) throw new Error("Manfiy summa bo'lmaydi"); // invariant konstruktorda
Object.freeze(this);
}
add(o: Money): Money { // MUTATSIYA yo'q -> yangi obyekt
if (this.currency !== o.currency) throw new Error("Valyuta mos emas");
return new Money(this.amount + o.amount, this.currency);
}
}
Aggregate — izchillik chegarasi
Aggregate — birga o‘zgaradigan entity/VO’lar to‘plami, bitta root bilan. Tashqi kod faqat rootga murojaat qiladi; invariantlar (qoidalar) root ichida saqlanadi:
// AGGREGATE — izchillik chegarasi; ROOT orqali kirasan, invariantlar ICHKARIDA saqlanadi
export class Order { // <-- Aggregate Root
private items: OrderItem[] = [];
private constructor(readonly id: OrderId, private status: OrderStatus) {}
addItem(product: ProductId, qty: number) { // faqat root orqali o'zgaradi
if (this.status !== "draft") throw new Error("Faqat draft'ga qo'shiladi"); // invariant
this.items.push(new OrderItem(product, qty));
}
// boshqa aggregate'ga OBYEKT bilan emas, ID bilan murojaat: customerId, productId
}
Aggregate — tranzaksiya chegarasi: bitta aggregate = bitta mahalliy tranzaksiya (13-mavzu). U tabiiy sharding birligi ham (6-mavzu — aggregate ID = shard key). To‘g‘ri aggregate chegaralari ko‘p taqsimlangan tranzaksiya muammosini oldindan yo‘qotadi.
Bounded Context va Domain Events
bounded contextTaktik naqshlar "ichki" edi. Bounded Context — strategik: katta tizimni mustaqil modellarga ajratadi. Bu to‘g‘ridan-to‘g‘ri mikroservis chegaralariga (1-mavzu) ulanadi.
Bounded Context — model chegarasi
Bir xil so‘z turli kontekstda turli narsani bildiradi. "Order" Sotuv kontekstda — savat, narx, chegirma; Yetkazib berish kontekstda — manzil, og‘irlik, kuryer. Ularni bitta ulkan modelga tiqishtirmaslik kerak — har kontekst o‘z modeliga ega:
Domain Events — kontekstlarni bog‘lash
Domain event — domenda yuz bergan muhim voqea (OrderConfirmed). U aggregate va kontekstlarni bo‘shashtiradi: Sotuv "tasdiqlandi" eventini chiqaradi, Yetkazish uni tinglaydi — bir-birini to‘g‘ridan-to‘g‘ri bilmaydi:
// DOMAIN EVENT — domenda muhim narsa yuz berdi (aggregate ichida qayd qilinadi)
class Order {
confirm() {
if (this.status !== "draft") throw new Error("Faqat draft tasdiqlanadi");
this.status = "confirmed";
this.record(new OrderConfirmed(this.id, this.total)); // event ro'yxatga olindi
}
}
// use case commit qilganda -> event'lar publish qilinadi (3, 10-mavzu bilan ulanadi)
Bounded context = mikroservis chegarasi (1-mavzu). Domain event = event-driven (3-mavzu) va event sourcing (10-mavzu) ning domen ildizi. Ya’ni bu naqshlar tasodifan emas — ular yaxshi domen dizaynidan tabiiy o‘sib chiqadi.
Anemic vs Rich domain model
rich modelClean Architecture domen sof bo‘lsin deydi — lekin sof degani boy (rich) degani. Ko‘p loyiha bu yerda adashib, anemik modelga tushadi.
// ANEMIC (anti-pattern) — entity shunchaki ma'lumot; mantiq servisda (protsedural)
class Order { status: string; } // faqat data
class OrderService {
cancel(o: Order) {
if (o.status === "confirmed") throw new Error("..."); // qoida TASHQARIDA
o.status = "cancelled";
}
}
// RICH — entity O'Z qoidalarini o'zi biladi (haqiqiy OO, clean arch maqsadi)
class Order {
cancel() {
if (this.status === "confirmed") throw new Error("..."); // qoida ICHKARIDA
this.status = "cancelled";
}
}
| Anemic model | Rich model | |
|---|---|---|
| Entity | Faqat ma’lumot (getter/setter) | Ma’lumot + xatti-harakat |
| Mantiq qayerda | Servislarda (protsedural) | Entity ichida (OO) |
| Qoidalar | Tarqoq, takrorlanadi | Bir joyda, himoyalangan |
| Baho | Fowler: anti-pattern | Clean arch maqsadi |
Anemik modelda order.status = "cancelled" ni istalgan joyda qilish mumkin — qoida chetlab o‘tiladi, takrorlanadi, buziladi. Rich modelda bekor qilishning yagona yo‘li — order.cancel(), va u qoidani majburlaydi. Domen shunda haqiqatan "biznes qoidalarini himoya qiluvchi qal’a" bo‘ladi.
Oddiy CRUD (qoida deyarli yo‘q — shunchaki saqlash/o‘qish)da anemik model maqbul — sun’iy "rich" qilish ortiqcha. Rich model haqiqiy biznes qoidalari bor domen uchun.
Mapping strategiyalari
mapper · 3 modelDomenni sof tutishning narxi bor: mapping. Uch xil model bor — DB, domen, API — va ular orasida tarjima kerak.
// MAPPER — qatlamlar orasida tarjima (domen sof qoladi, Prisma/DTO'ga bog'lanmaydi)
class OrderMapper {
// DB row -> domen (rehydrate)
static toDomain(row: PrismaOrder): Order {
return Order.rehydrate(row.id, row.status, row.items);
}
// domen -> DB shakli (persistence)
static toPersistence(o: Order): Prisma.OrderUpdateInput {
return { status: o.status };
}
// domen -> API javob (DTO) -> ichki modelni tashqariga oshkor qilmaydi
static toDto(o: Order): OrderDto {
return { id: o.id, status: o.status, total: o.total.amount };
}
}
Nega uch model? Prisma model DB sxemasiga bog‘langan; DTO API shakliga; ikkalasi ham o‘zgarishi mumkin. Domen ularga bog‘lansa — DB yoki API o‘zgarganda domen ham buziladi. Mapper domenni himoya qiladi.
Mapping — boilerplate, bu haqiqat. Kichik/oddiy loyihada persistence va domen modelini birlashtirish (bitta model) maqbul — mapping narxi foydasidan oshib ketsa. Murakkab/uzoq loyihada esa ajratish o‘zini oqlaydi: DB migratsiyasi domenga tegmaydi. Qaror — kontekstga bog‘liq.
Screaming architecture va use case chegaralari
screaming · use casePapka tuzilmasi ham arxitekturaning bir qismi. Screaming Architecture (Uncle Bob): tuzilma domenni baqirsin, framework’ni emas.
Domen bo‘yicha, texnik qatlam bo‘yicha emas
Ko‘p loyiha papkalarni controllers/, services/, models/ deb ajratadi — bu frameworkni baqiradi. Yaxshiroq: domen bo‘yicha (orders/, payments/) — loyihaga qarab "bu buyurtma tizimi" ekanini darhol bilasan:
src/
orders/ # <-- domen "baqiradi", framework emas
domain/ # Order (entity), Money (VO), OrderRepository (port)
application/ # CancelOrderUseCase, ConfirmOrderUseCase
infrastructure/ # PrismaOrderRepository, OrderController
payments/
domain/ application/ infrastructure/
shipping/
domain/ application/ infrastructure/
Use case chegaralari
Har use case — bitta ilova amali, aniq kirish va chiqish chegarasi bilan. Kirish (request model), chiqish (response model), bitta execute(). Bu use case’larni fokuslangan va mustaqil tutadi:
- Bitta use case = bitta amal (CancelOrder, ConfirmOrder — alohida, "OrderService" ichida hammasi emas).
- Aniq input/output — use case HTTP/DB shaklini bilmaydi, o‘z request/response modeliga ega.
- Controller — HTTP’ni use case input’iga tarjima qiladi, boshqa hech narsa.
Bu tuzilma bilan yangi dasturchi loyihaga kelib, orders/application/ni ochib — tizim nima qila olishini (barcha use case’lar ro‘yxati) darhol ko‘radi. Biznes imkoniyatlari kod tuzilmasidan "baqirib" turadi.
Qatlamlar bo‘yicha test strategiyasi
test piramidasiClean Architecture’ning eng katta amaliy foydasi — test. Har qatlam o‘z uslubi bilan sinaladi va test piramidasiga tabiiy mos keladi.
// DOMAIN — sof unit test (tez, mock YO'Q, qoidani to'g'ridan-to'g'ri sinaydi)
const order = Order.create();
order.confirm();
expect(() => order.cancel()).toThrow("Tasdiqlangan buyurtma bekor bo'lmaydi");
// USE CASE — mock port (in-memory repository), orkestratsiyani tekshiradi
const repo = new InMemoryOrderRepository([draftOrder]);
await new CancelOrderUseCase(repo).execute(draftOrder.id);
expect((await repo.findById(draftOrder.id)).status).toBe("cancelled");
// ADAPTER — integratsiya test (HAQIQIY DB, testcontainers) — mapping to'g'riligini
| Qatlam | Test turi | Xususiyat |
|---|---|---|
| Domain | Sof unit | Tez, mock yo‘q, qoidani to‘g‘ridan-to‘g‘ri |
| Use Case | Unit + mock port | In-memory repo, orkestratsiya tekshiriladi |
| Adapter | Integratsiya | Haqiqiy DB (testcontainers), mapping |
An’anaviy kodda biznes qoidasini sinash uchun DB + HTTP + framework kerak — sekin, mo‘rt, kam. Clean Architecture’da domen va use case testlari millisekundda, hech qanday infratuzilmasiz ishlaydi. Natijada ko‘p, ishonchli test — bu esa qo‘rqmasdan refactoring qilish erkinligini beradi.
Qachon qoidalarni buzish + kurs yakuni
Yakuniy senior saboq: qoidalar maqsadga xizmat qiladi, aksincha emas. Qachon ularni ongli ravishda buzishni bilish — haqiqiy yetuklik.
Qachon qoidalarni buzish mumkin
- Oddiy CRUD: qoida deyarli yo‘q ilovada to‘liq qatlam/mapping — ortiqcha. Prismani use case’ga to‘g‘ridan-to‘g‘ri ishlatish maqbul.
- Prototip / MVP: tezlik muhimroq; keyin domen murakkablashsa — qatlamla.
- Kichik jamoa/skript: bilvositalik narxi foydasidan oshsa — soddaroq tuzilma.
Clean Architecture — dogma emas, vosita. Maqsad: saqlanadigan, testlanadigan, o‘zgarishga chidamli kod. Agar biror qoida sizning kontekstingizda qiymat qo‘shmay, faqat narx keltirsa — uni ongli ravishda buzing. Yomon dasturchi qoidani bilmasdan buzadi; yaxshi dasturchi bilib buzadi; senior — qachon buzish, qachon saqlashni biladi.
Siz tizim dizaynining to‘liq yo‘lini bosib o‘tdingiz:
• Struktura (1–3): Mikroservis, Modular Monolith, Event-Driven
• Masshtab (4–8): Caching, Load Balancing, Sharding, Replication, API Gateway
• Ma’lumot naqshlari (9–10): CQRS, Event Sourcing
• Nazariya & ko‘rinish (11–12): CAP Theorem, Observability
• Ishonchlilik (13–14): Distributed Transactions, Resilience Engineering
• Tashkil (15): Clean Architecture — hammasini birlashtiruvchi
Har mavzu Core (tushunish) va Deep/Senior (chuqur mexanika) qatlamlari bilan. Endi sizda junior’dan senior gacha mustahkam, amaliy (NestJS) poydevor bor.
🚀 Tabriklaymiz — butun kurs tugadi! 15 mavzu × 2 qatlam. Bilim — qurishda mustahkamlanadi: bu naqshlarni real loyihalaringizda qo‘llang, va tizimlaringiz uzoq, o‘zgaruvchan, lekin buzilmasdan yashasin.
Omad, Dinmuhammad! Savollar bo‘lsa — har doim shu yerdaman.