Bu qatlamda nima bor
Core darslik asoslarni berdi. Bu Deep qatlam senior darajadagi chuqur mexanikani ochadi: modul chegarasini bounded context bo‘yicha chizish va uni compile-time (public API + lint/arch test) majburlash, modullararo aloqa (interfeys vs ichki event), alohida sxema va bitta ACID tranzaksiya ustunligi, vertical slice tuzilma, strangler fig bilan mikroservisga ajratish, hamda qachon modular monolith to‘g‘ri tanlov ekani. Oxirida — yig‘ma reference va senior cheklist.
Modul chegarasi
bounded context · koheziyaCore’da "chegarani majburla" dedik. Senior savoli — chegara qayerdan o‘tadi? Noto‘g‘ri modul chegarasi monolitni "big ball of mud"ga aylantiradi.
Modul = bounded context
Modul chegarasi tasodifan emas — u biznes subdomeni / bounded context (15-mavzu) bo‘yicha o‘tadi: Buyurtma, To‘lov, Inventar. Bu — mikroservis chegarasi bilan bir xil (6-bo‘limda ko‘ramiz: shuning uchun ko‘chirish oson).
Yuqori koh'eziya: birga o‘zgaradigan kod bitta modulda. Past bog‘lanish: modullar orasidagi bog‘liqlik minimal va faqat ochiq interfeys orqali. Va bog‘liqlik siklsiz (A→B→A bo‘lmasin) — aks holda ular aslida bitta modul.
Chegarani compile-time majburlash
public API · lint/arch testModul chegarasi hujjatda emas, kompilyatorda majburlanishi kerak — aks holda vaqt o‘tib buziladi ("shunchaki bu importni qo‘shaman").
Public API — barrel export
Har modul faqat o‘z shartnomasini ochadi (bitta index.ts), ichki fayllarini yashiradi. Boshqa modul faqat shu ochiq yuzaga murojaat qiladi:
// Har modul FAQAT o'z public shartnomasini ochadi (barrel: index.ts)
// payments/index.ts <-- yagona kirish nuqtasi
export { PaymentApi } from "./payment.api"; // ochiq interfeys
export type { PaymentResult } from "./contracts"; // ochiq DTO
// payment.service.ts, payment.entity.ts -> EKSPORT QILINMAYDI (ichki)
Architectural fitness function — buzilishni avtomatik ushlash
Chegarani lint qoidasi/arxitektura testi bilan majburla — kimdir boshqa modulning ichki qismiga kirsa, CI buzadi:
// ESLint bilan modul chegarasini MAJBURLASH (eslint-plugin-boundaries)
// .eslintrc — modul ICHKI qismiga to'g'ridan-to'g'ri kirish TAQIQLANADI
"rules": {
"boundaries/element-types": ["error", {
"default": "disallow",
"rules": [
{ "from": "orders", "allow": ["orders", "shared", "payments-api"] }
// Buyurtma faqat To'lovning PUBLIC API'siga kira oladi,
// uning ichki fayllariga (payments/internal/*) EMAS
]
}]
}
Modular monolith modul chegarasi bilan tirik yoki o‘lik bo‘ladi. Chegara faqat "kelishuv" bo‘lsa — deadline bosimida buziladi va monolit "loyga" aylanadi. Compile-time majburlash (lint/test) uni haqiqiy tutadi. Bu — modular monolithni oddiy monolitdan ajratadigan yagona narsa.
Modullararo aloqa
interfeys vs ichki eventModullar bitta jarayonda, lekin ular qanday gaplashadi? Ikki usul bor — va tanlov kelajakdagi mikroservisga ko‘chirish osonligini belgilaydi.
// 1) TO'G'RIDAN-TO'G'RI — public interfeys orqali (sinxron, oddiy)
class OrderService {
constructor(private payments: PaymentApi) {} // faqat PUBLIC API'ga bog'liq
async confirm(id: string) {
await this.payments.charge(id); // to'g'ridan-to'g'ri chaqiruv (bir jarayon)
}
}
// 2) ICHKI EVENT — bo'shashtirish uchun (async, in-process event bus)
this.eventBus.publish(new OrderConfirmed(order.id)); // Buyurtma e'lon qiladi
@OnEvent("order.confirmed") // Yetkazish tinglaydi
handle(e: OrderConfirmed) { this.scheduleDelivery(e.orderId); }
// modullar bir-birini BILMAYDI -> keyin mikroservisga ko'chirish oson
| To‘g‘ridan-to‘g‘ri chaqiruv | Ichki event | |
|---|---|---|
| Bog‘lanish | Zichroq (interfeysga) | Bo‘sh (bir-birini bilmaydi) |
| Uslub | Sinxron, sodda | Asinxron, moslashuvchan |
| Qachon | Javob darhol kerak | "Sodir bo‘ldi" xabari, yon ta’sir |
| Ko‘chirish | REST/gRPC ga aylanadi | Message broker’ga aylanadi (deyarli tekin) |
Modullar bir-birining entity/jadvalini to‘g‘ridan-to‘g‘ri ishlatmasligi kerak — faqat DTO/shartnoma orqali. Buyurtma moduli To‘lov modulining Payment entity’sini import qilsa — chegara buzildi, ko‘chirish imkonsiz bo‘ladi.
Ma‘lumot va tranzaksiya
alohida sxema · ACIDModular monolithning eng katta yutug‘i — ma’lumot. Bitta DB’da, lekin toza chegaralar bilan — mikroservisning ko‘p og‘rig‘isiz.
Bitta DB, alohida sxema
- Har modul o‘z jadvallariga egalik qiladi — boshqa modul ularga to‘g‘ridan-to‘g‘ri tegmaydi (sxema/nom-fazo bilan ajrat).
- Modullar aro FK yo‘q (yoki faqat mantiqiy) — jadval bog‘liqligi chegarani buzadi va kelajakda ajratishni imkonsiz qiladi.
- Cross-module JOIN qilma — boshqa modul ma’lumoti kerak bo‘lsa, uning public APIsidan so‘ra.
Mikroservisda bir amal ko‘p servisga tegsa — Saga, kompensatsiya, eventual consistency (13-mavzu — murakkab). Modular monolithda hammasi bitta jarayon, bitta DB: oddiy BEGIN…COMMIT hammasini atomik qiladi. Bu — ulkan soddalik, va aynan shuning uchun ko‘p loyiha uchun modular monolith to‘g‘ri tanlov.
Agar modul kelajakda ajratilsa — uning jadvallari allaqachon alohida sxemada, FK’siz, o‘z API’si ortida. Ya’ni ajratish mexanik, arxitekturani qayta yozish emas.
Vertical slice tuzilma
mini clean-archKodni texnik qatlam bo‘yicha (controllers/, services/) emas, modul (vertical slice) bo‘yicha tashkil qil — bu screaming architecture (15-mavzu) modular monolithda.
Har modul — o‘z ichida to‘liq (domain + application + infrastructure), va o‘z index.ts public API’si bilan. Bu tuzilma modul mustaqilligini ko‘rsatadi va ajratishni osonlashtiradi:
src/
modules/
orders/ # <-- MODUL = vertical slice (o'z mini clean-arch, 15-mavzu)
domain/ # Order entity, qoidalar
application/ # use case'lar
infrastructure/ # repo, controller
index.ts # <-- PUBLIC API (barrel) — faqat shu ochiq
payments/
domain/ application/ infrastructure/ index.ts
inventory/
...
shared/ # umumiy kernel (juda kam, ehtiyotkorlik bilan)
15-mavzu (Clean Architecture) modul ichida yashaydi: har modulning o‘z domeni (sof qoidalar), use case’lari va infratuzilma adapter’lari bor. Modular monolith — bu tashqi tuzilma; Clean Architecture — ichki tuzilma. Ikkalasi birga: modullar aro toza chegara + modul ichida toza qatlamlar.
Strangler fig bilan ajratish
strangler · ACLModular monolithning kuchi — u sizni qamamaydi. Ehtiyoj tug‘ilsa, modulni strangler fig naqshi bilan mikroservisga ajratasan.
Strangler fig — bosqichma-bosqich ajratish
Butun tizimni qayta yozish o‘rniga — bitta modulni o‘z chegarasi bo‘yicha ajratib, uning trafigini asta yangi servisga yo‘naltirasan. Modul chegarasi aniq bo‘lgani uchun (2, 4-bo‘limlar) — bu mexanik jarayon:
Ajratishda anti-corruption layer (ACL) qo‘y: yangi servis va qolgan monolit orasida tarjimon qatlam — biri ikkinchisining modelini "ifloslantirmasin". Public API allaqachon shartnoma bo‘lgani uchun, ACL tabiiy o‘rnashadi.
Qachon modular monolith yutadi
trade-offSenior haqiqat: aksariyat loyihaga mikroservis kerak emas. Modular monolith — ko‘pincha eng oqilona tanlov, ayniqsa boshlanishda.
Modular monolith yutadi
- Bitta deploy, sodda operatsiya — bitta artefakt, mesh/discovery yo‘q
- Bitta ACID tranzaksiya — Saga murakkabligi yo‘q (4-bo‘lim)
- Oson debug/test — bitta jarayon, tarmoq yo‘q
- Arzon refactoring — chegaralarni bir repoda qayta chizish oson
- Optionality — keyin kerak bo‘lsa, ajratasan (strangler)
Cheklovlari
- Yagona deploy birligi — kichik o‘zgarish uchun ham butunni deploy
- All-or-nothing masshtab — bitta modul emas, butunini masshtablaysan
- Yagona texnologiya — hamma modul bir stack
- Jamoa masshtabi — juda katta jamoada deploy to‘qnashuvi
Modular monolithdan boshla (toza chegaralar bilan). Faqat haqiqiy og‘riq paydo bo‘lganda (bitta modul mustaqil masshtab talab qilsa, jamoa deploy uchun to‘qnashsa) — o‘sha bitta modulni ajrat. "Mikroservis birinchi"dan qoch — u ko‘p loyihani keraksiz murakkablikka cho‘ktiradi. Chegaralar toza bo‘lsa, kechikish bepul.
Yig‘ma reference va senior cheklist
Modular monolith — yig‘ma reference
| Qism | Amaliyot | Bo‘lim |
|---|---|---|
| Modul chegarasi | Bounded context bo‘yicha, koh'eziya/bog‘lanish | 1 |
| Chegara majburlash | Public API (barrel) + lint/arch test | 2 |
| Modullararo aloqa | Public interfeys yoki ichki event (umumiy entity yo‘q) | 3 |
| Ma‘lumot | Alohida sxema, FK/JOIN yo‘q, bitta ACID tranzaksiya | 4 |
| Tuzilma | Vertical slice, har modul mini clean-arch | 5 |
| Evolyutsiya | Strangler fig + ACL bilan ajratish | 6 |
Senior tayyorlik cheklisti
| Savol / amaliyot | Bo‘lim |
|---|---|
| Modul chegaralarini bounded context bo‘yicha chizaman (koh'eziya/past bog‘lanish, siklsiz) | 1 |
| Chegarani public API + lint/arch test bilan compile-time majburlayman | 2 |
| Modullar aro faqat interfeys/event bilan gaplashaman; umumiy entity ishlatmayman | 3 |
| Har modulga alohida sxema; modullar aro FK/JOIN yo‘q; ACID tranzaksiya ustunligini ishlataman | 4 |
| Kodni vertical slice bo‘yicha, har modul mini clean-arch qilib tashkil qilaman | 5 |
| Kerak bo‘lganda modulni strangler fig + ACL bilan ajrataman | 6 |
| Mikroservisga o‘tish ehtiyojini to‘g‘ri baholayman (default — modular monolith) | 7 |
Bu 2-mavzuning Deep/Senior qatlami edi — Core bilan birga, Modular Monolith endi to‘liq.
Bog‘liq mavzular: 1-mavzu (Microservices — keyingi qadam), 3 (Event-Driven — ichki event), 13 (Distributed Transactions — nega monolit soddaroq), 15 (Clean Architecture — modul ichi).