Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. Uch qatlam — uch savol
- 3. Interfeys
- 3.1 VazifaOmbori
- 3.2 Natija turi — xato emas
- 4. Token: interfeys ish vaqtida yo'q
- 5. Almashtirish — bitta interfeys, besh amalga oshirish
- 6. Shartnoma testlari — bitta to'plam, hamma ombor
- 6.1 G'oya
- 6.2 Biz ishga tushirdik
- 7. Uch tipik xato
- 7.1 Semiz controller
- 7.2 Anemik xizmat
- 7.3 Oqib chiquvchi ombor
- 8. Hujumchi nigohi
- 9. Express'da qanday edi, Nest'da qanday
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): qaysi qatlam?
- 2-mashq (o'rta): kichik shartnoma va ikki ombor
- 3-mashq (qiyin): keshli o'ram
- 12. Loyiha qadami: o'zgarish yo'q — tekshiruv
- 13. Real ishda
- Xulosa
- Manbalar
NestJS repository naqshi: interfeys, token va qatlamlar chegarasi
Qisqacha: Repository (ombor) — ma'lumot qayerda va qanday saqlanishini biladigan yagona qatlam. Xizmat (service) esa faqat biznes qoidalarini biladi va omborni interfeys orqali ko'radi. Nest'da interfeys ish vaqtida yo'q, shuning uchun bog'lanish
Symboltoken bilan:@Inject(VAZIFA_OMBORI) ombor: VazifaOmbori. Natija: ombor almashadi (xotira, fayl,pg, Prisma, Redis kesh), xizmat va controller o'zgarmaydi, ORM turlari (BigInt, entity) tashqariga chiqmaydi, hamma ombor bitta shartnoma testidan o'tadi.vazifalar-apishu naqsh ustiga qurilgan.
Bu darsda
- Controller, xizmat va ombor orasidagi chegarani aniq ayta olasiz: qaysi kod qayerda yashaydi.
- Repository interfeysi va
Symboltoken bilan ombor almashtirishni tushuntirasiz. - ORM turlarini xizmatdan yashirish nega muhimligini va natija turlari (
YozishNatijasi) nima berishini bilasiz. - Bitta shartnoma testlari to'plami bir nechta amalga oshirishni qanday tekshirishini o'z ko'zingiz bilan ko'rasiz.
- "Semiz controller", "anemik xizmat" va "oqib chiquvchi ombor" kabi xatolarni tanib olasiz.
Oldin bilishingiz kerak: express.Router va qatlamli loyiha tuzilmasi, Repository va SQL'ni testlash, Custom provider'lar, Tranzaksiyalar NestJS'da.
O'tgan besh darsda bitta savol qayta-qayta chiqdi: "nega kanon bu ORM'ni loyihaga qo'shmadi?" Javobning yarmi — ORM tanlovi (27-qism). Ikkinchi yarmi — shu dars: loyiha ORM'ga emas, interfeysga bog'langan.
1. Nega bu kerak?
25-qismda kodni qatlamlarga bo'lgandik: router → xizmat → ombor (qatlamli tuzilma). 26-qismda xotiradagi ombor Postgres'ga almashdi, 27-qismda — Prisma va Redis kesh qo'shildi. Har safar xizmat kodi bir qator ham o'zgarmadi. Bu tasodif emas, naqsh.
O'xshatish: devordagi elektr rozetka. Uyingizdagi choynak, telefon zaryadlovchisi, chang yutgich — hammasi bitta rozetka shakliga moslangan. Elektr qayerdan kelishi — GES, quyosh paneli yoki generator — choynakni qiziqtirmaydi. Rozetka — interfeys, elektr manbalari — ombor amalga oshirishlari, choynak — xizmat.
Nega bu Nest darsida? Chunki Nest bu naqshni majburiy qilmaydi. PrismaService ni to'g'ridan xizmatga inject qilish (Prisma bilan NestJS) — Nest hujjatidagi eng ko'p uchraydigan yo'l. U ishlaydi. Lekin keyin ombor almashtirib bo'lmaydi, xizmat test uchun haqiqiy bazasiz ishlamaydi va Prisma turlari controller'gacha yetadi.
2. Uch qatlam — uch savol
| Qatlam | Javob beradigan savol | vazifalar-api da |
|---|---|---|
| Controller | "HTTP'da nima keldi va nima qaytadi?" | status, sarlavha, ETag, Location, 207 |
| Xizmat | "Biznes qoidasi nima deydi?" | 1000 ta chegara, takror matn, log, webhook |
| Ombor | "Qayerda va qanday saqlanadi?" | SQL, Prisma, fayl, kesh |
Har qatlam faqat pastidagini biladi. Controller ombordan bexabar, ombor HTTP'dan bexabar. Bir misol — o'tgan darsdagi /toplam: 207 qarori controller'da (HTTP tili), "takror matn — kutilgan xato" qarori xizmatda, takrorni topish (EXCLUDE cheklovi, 26-qism) — bazada va omborda.
3. Interfeys
3.1 VazifaOmbori
src/vazifalar/ombor.ts (kanon; telefon uchun formatlandi, izohlar qisqartirildi):
export type YozishNatijasi<T> =
| { tur: "ok"; qiymat: T }
| { tur: "topilmadi" }
| { tur: "konflikt"; joriy: Vazifa };
export interface VazifaOmbori {
royxat(sorov: RoyxatSorovi): Promise<RoyxatNatija>;
/** filtrga mos vazifalar soni (limit va sahifasiz) */
soni(filtr: Filtr): Promise<number>;
ol(id: number): Promise<Vazifa | undefined>;
yarat(yangi: YangiVazifa): Promise<Vazifa>;
/** versiya +1; shart berilsa va versiya boshqa — "konflikt" */
yangila(
id: number, ozgarish: VazifaOzgarishi, shart?: Shart,
): Promise<YozishNatijasi<Vazifa>>;
ochir(id: number, shart?: Shart): Promise<YozishNatijasi<null>>;
/** teglar va nechta vazifada — ko'pidan kamiga */
teglar(): Promise<TegSoni[]>;
/** butun to'plamni almashtirish (PUT /v1/vazifalar). Atomik */
almashtirHammasi(
vazifalar: readonly AlmashtirishVazifasi[],
): Promise<{ vazifalar: Vazifa[]; versiya: number }>;
}Interfeysga diqqat bilan qarang — unda nima yo'q:
- SQL yo'q,
PrismaClientyo'q,pg.Poolyo'q. BigIntyo'q:id: number. PrismaBigIntqaytaradi,pg— satr (TypeORM darsidagibiginttuzog'i). Har ombor o'zinumberga aylantiradi.- Sana
Dateemas:Vazifa.yaratildi— ISO satr (UTC,Zbilan). Bazadan qaytgantimestamptzham, fayldan o'qilgan qiymat ham bir xil shaklga keladi. - Hamma metod
Promise— xotiradagi ombor ham. 26-qismda shunday qaror qilingan: sinxron metodli interfeys keyin asinxron bazaga almashtirilmaydi.
3.2 Natija turi — xato emas
yangila va ochir xato tashlamaydi, YozishNatijasi qaytaradi: ok, topilmadi yoki konflikt (Discriminated union). Uni HTTP xatosiga xizmat aylantiradi:
/** Ombor natijasini qiymatga yoki xatoga aylantiradi:
* topilmadi → 404, versiya mos emas → 412 */
function natija<T>(id: number, n: YozishNatijasi<T>): T {
if (n.tur === "topilmadi") throw topilmadi(id);
if (n.tur === "konflikt") {
const v = String(n.joriy.versiya);
throw new ShartXatosi(
`Vazifa ${String(id)} boshqa so'rov bilan o'zgargan ` +
`(joriy versiya ${v}) — qayta o'qing`,
`"${v}"`,
);
}
return n.qiymat;
}Nega shunday? "Topilmadi" va "versiya mos emas" — ombor uchun oddiy natija, favqulodda hodisa emas. Ombor 404 yoki 412 haqida bilmasligi kerak — bu HTTP tili. U faqat "qator yo'q" yoki "versiya boshqa" deydi. ShartXatosi ga aylantirish va xabarni yozish — xizmatning ishi (Optimistic concurrency).
Tekshirib ko'ring:
PrismaOmbori.yangilaichida PrismaP2025(yozuv topilmadi) xatosini oldi. U nima qilishi kerak: xatoni tashlashmi yoki boshqa narsa?
Javob
{ tur: "topilmadi" } qaytarishi kerak. P2025 — Prisma'ning ichki tili; uni tashqariga chiqarsa, xizmat Prisma xatolarini bilishga majbur bo'ladi va boshqa omborlar (xotira, pg) bilan bir xil ishlamaydi. Interfeys va'da qilgan natija — YozishNatijasi. Xizmat esa uni 404 ga aylantiradi.
4. Token: interfeys ish vaqtida yo'q
TypeScript interface kompilyatsiyadan keyin yo'qoladi — JavaScript'da VazifaOmbori degan narsa yo'q. Shuning uchun Nest uni constructor turidan taniy olmaydi: design:paramtypes da Object bo'ladi (DI va IoC konteyner). Yechim — token:
// tokenlar.ts
export const VAZIFA_OMBORI = Symbol("VAZIFA_OMBORI");
// vazifalar.service.ts
@Injectable()
export class VazifalarService {
constructor(
@Inject(VAZIFA_OMBORI) private readonly ombor: VazifaOmbori,
@Inject(LOG) private readonly log: Log,
) {}
}@Inject(VAZIFA_OMBORI) — "shu tokendagi qiymat"; : VazifaOmbori — TypeScript uchun va'da. O'tgan darsdan oldingi darsda ko'rgan tuzoq shu yerda ham bor: token va tur orasidagi bog'lanishni TypeScript tekshirmaydi. Kanonda bu xavf kichik — VAZIFA_OMBORI faqat bitta joyda (OmborModule) beriladi va uning qiymati doim VazifaOmbori ni qaytaradigan fabrikadan.
5. Almashtirish — bitta interfeys, besh amalga oshirish
flowchart TB
S["VazifalarService"] --> I["VazifaOmbori (interfeys)"]
I --> X["XotiraOmbori"]
I --> F["FaylOmbori"]
I --> P["PgOmbori (pg)"]
I --> R["PrismaOmbori"]
I --> K["KeshliOmbori (Redis)"]
K -. "ichida" .-> PXizmat faqat o'rtadagi to'rtburchakni ko'radi. Pastdagi beshtasi — implements VazifaOmbori. Qaysi biri ishlashini OmborModule hal qiladi:
- Ilovada —
OmborModule.forRootAsync():SAQLASHconfig qiymati bo'yicha (xotira,fayl,postgres,prisma) vaREDIS_URLbo'lsa — ustiga kesh (Prisma bilan NestJS darsida ko'rdik). - Testda —
OmborModule.forRoot(new XotiraOmbori()): tayyor nusxa, baza kerak emas.
KeshliOmbor dan chiqqan punktir chiziqqa qarang. Kesh — alohida ombor emas, boshqa ombor ustidagi o'ram: constructor'da ichki VazifaOmbori ni oladi (new KeshliOmbor(ichki, redis, egasiId, sozlama)), o'qishni keshdan, yozishni ichkisiga beradi. Interfeys bir xil bo'lgani uchun o'ram ham VazifaOmbori. Bu — dekorator naqshi (Nest'ning @ dekoratorlari bilan aralashtirmang: bu yerda gap obyektni o'rash haqida).
6. Shartnoma testlari — bitta to'plam, hamma ombor
6.1 G'oya
Besh amalga oshirish bor. Har biri uchun alohida test yozsak — beshta to'plam, va ular bir-biridan farqlanib ketadi. 25-qismda boshqa yo'l tanlangan: shartnoma testi (contract test) — bitta funksiya, u istalgan omborni olib, bir xil talablarni tekshiradi (Backend test strategiyasi):
export function omborShartnomasi(
nom: string,
yarat: () => Promise<VazifaOmbori>,
): void {
describe(`VazifaOmbori shartnomasi: ${nom}`, () => {
test("yarat: id 1 dan, teglar matndan, …", async () => {
const o = await yarat();
const v = await o.yarat({ matn: "Non olish #Uy #uy" });
assert.equal(v.id, 1);
assert.deepEqual(v.teglar, ["uy"]);
// ...
});
// yangila, ochir, royxat, almashtirHammasi
});
}(test/ombor-shartnoma.ts ning boshi, qisqartirildi.) Har amalga oshirish bitta qator bilan ulanadi:
omborShartnomasi("xotira", () => Promise.resolve(new XotiraOmbori()));
omborShartnomasi("postgres", async () => {
/* bo'sh baza + PgOmbori */
});6.2 Biz ishga tushirdik
vazifalar-api ning shu holatida, o'z PG 17 bazamizda (biz ishga tushirdik; Redis keshli variant — Redis'siz o'tkazib yuborildi). Shartnoma testlari ro'yxati (vaqtlar olib tashlandi, nomlar qisqartirildi):
✓ VazifaOmbori shartnomasi: xotira > yarat: id 1 dan, teglar matndan, …
✓ VazifaOmbori shartnomasi: xotira > yangila: versiya +1, shart mos kelmasa konflikt, …
✓ VazifaOmbori shartnomasi: xotira > ochir: shart, topilmadi, to'plam versiyasi …
✓ VazifaOmbori shartnomasi: xotira > royxat: filtr, saralash, offset, kursor, jami; …
✓ VazifaOmbori shartnomasi: xotira > almashtirHammasi: massiv tartibi = tartib, …
✓ VazifaOmbori shartnomasi: fayl > … (o'sha 5 test)
✓ VazifaOmbori shartnomasi: postgres > … (o'sha 5 test)
✓ VazifaOmbori shartnomasi: prisma > … (o'sha 5 test)
Test Files 3 passed (3)
Tests 42 passed (42)To'rt ombor × 5 shartnoma testi = 20; qolgan 22 — har omborning o'ziga xos testlari (masalan, pg da statement_timeout, Prisma'da xato kodlari). Kanonda beshinchisi — Redis keshli — ham shu to'plamdan o'tadi.
Bu natijaning ma'nosi: xizmat qaysi omborni olishidan qat'i nazar, yarat id 1 dan boshlaydi, konflikt versiya mos kelmaganda qaytadi, kursor bir xil ishlaydi. Xizmat testlari xotiradagi ombor bilan yozilsa, ular Postgres bilan ham to'g'ri — chunki shartnoma buni kafolatlaydi.
7. Uch tipik xato
7.1 Semiz controller
// ❌ controller'da biznes qoidasi va SQL
@Post()
async yarat(@Body() b: YangiVazifa) {
const { rows } = await this.pool.query(
"SELECT count(*)::int AS n FROM vazifalar", [],
);
if (rows[0].n >= 1000) throw new ConflictException("To'ldi");
// ...
}Controller HTTP'ni ham, biznesni ham, SQL'ni ham biladi. Bu qoidani (1000 ta chegara) boshqa joyda — masalan, /toplam yoki fon importda — qayta yozishga to'g'ri keladi va ular bir-biridan farqlanadi. Kanonda chegara xizmatdagi MAKS_VAZIFA da, bitta joyda.
7.2 Anemik xizmat
// ❌ xizmat faqat uzatadi — mantiq boshqa joyda
royxat(s: RoyxatSorovi) { return this.ombor.royxat(s); }
yarat(y: YangiVazifa) { return this.ombor.yarat(y); }Har metod bitta uzatish bo'lsa, xizmat bo'sh qatlam. Bu o'zi xato emas — kanonda royxat va teglar aynan shunday, chunki ularda qoida yo'q. Xato boshqa: qoida bor, lekin u omborda yoki controller'da yashaydi. Masalan, "vazifa bajarilganda webhook yuborish" omborga yozilsa — pg omborida bor, Prisma omborida unutildi.
7.3 Oqib chiquvchi ombor
// ❌ interfeys ORM turini qaytaradi
interface VazifaOmbori {
ol(id: bigint): Promise<Prisma.VazifaGetPayload<{}> | null>;
}Interfeys "abstrakt" ko'rinadi, lekin unda Prisma turi. Endi xizmat ham, controller ham Prisma'ni import qiladi; pg omborini yozish uchun Prisma turini qo'lda yasash kerak. Interfeys domen turida bo'lsin (Vazifa, number), ORM turida emas.
Maslahat: "Generic repository" —
Repository<T>bilanfindAll,findById,save— ko'p maqolada tavsiya qilinadi. Kanon uni ishlatmaydi:VazifaOmborining metodlari vazifa domeniga xos (teglar,almashtirHammasi, kursor bilanroyxat). Umumiy CRUD interfeysi bu amallarni yo ifoda eta olmaydi, yo ORM'ning o'zini qayta yozadi.
8. Hujumchi nigohi
Ombor — egalik tekshiruvining tabiiy joyi. Kanonda har ombor egasi bilan yaratiladi: new PgOmbori(pool, config.EGASI_ID), new PrismaOmbori(db, config.EGASI_ID). Ombor ichidagi har so'rov WHERE egasi_id = $… bilan cheklanadi. Xizmat yoki controller boshqa foydalanuvchining id sini bersa ham, ombor uni topmaydi — topilmadi.
Bu 28-qismdagi IDOR / BOLA himoyasining poydevori (RBAC va resurs egaligi). Agar egalik tekshiruvi controller'da bo'lsa, yangi endpoint qo'shgan dasturchi uni unutishi mumkin — va hujumchi GET /v1/vazifalar/49 bilan birovning vazifasini o'qiydi. Omborda bo'lsa — unutib bo'lmaydi: egasiz so'rov yozishning o'zi yo'q. 28-qism auth Nest'ga ko'chganda (30-qism oxirida) EGASI_ID o'rnini joriy foydalanuvchi oladi, ombor shartnomasi esa o'zgarmaydi.
9. Express'da qanday edi, Nest'da qanday
| 25–27 (Express) | Nest | |
|---|---|---|
| Interfeys | VazifaOmbori |
o'sha, o'zgarmagan |
| Xizmatga berish | vazifaXizmati(ombor, …) fabrika funksiya |
@Inject(VAZIFA_OMBORI) |
| Tanlash | server.ts da omborYarat |
OmborModule.forRootAsync |
| Testda | vazifaXizmati(new XotiraOmbori()) |
OmborModule.forRoot(new XotiraOmbori()) |
| Shartnoma testlari | node:test |
Vitest, o'sha matn |
Naqshning o'zi 25-qismdan; Nest faqat "kim kimga beradi" ni DI'ga topshirdi. Kanon o'lchovi bo'yicha Nest'ga ko'chishda src 2 653 qatordan 3 281 ga o'sdi — asosan dekoratorlar, modullar va token qatorlari hisobiga. Ombor kodi deyarli o'zgarmadi.
10. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Nest can't resolve … (?) interfeys turli parametrda |
interfeys ish vaqtida yo'q | Symbol token + @Inject |
Xizmatda import { Prisma } |
ombor ORM turini chiqargan | interfeys domen turida |
| Bir qoida ikki joyda, farqli | qoida controller/omborda | xizmatga ko'chiring |
| Yangi ombor boshqacha ishlaydi | shartnoma testi yo'q | bitta omborShartnomasi hamma uchun |
| Birovning yozuvi o'qildi | egalik controller'da, unutilgan | ombor egasi bilan yaratilsin |
11. Mashqlar
Mashqlar kurs/mashqlar/30/36-repository-naqshi/ da.
1-mashq (oson): qaysi qatlam?
To'ldiring (controller / xizmat / ombor):
ETagsarlavhasini qo'yish —- "1000 tadan ortiq vazifa bo'lmasin" qoidasi —
WHERE egasi_id = $1sharti —{ tur: "konflikt" }ni 412 ga aylantirish —
Yechim
- controller — HTTP sarlavhasi.
- xizmat — biznes qoidasi (
MAKS_VAZIFA). - ombor — saqlash va egalik.
- xizmat —
natija()funksiyasi; ombor HTTP'ni bilmaydi.
2-mashq (o'rta): kichik shartnoma va ikki ombor
Brauzerda ishlaydigan kichik interfeys: yarat(matn) → { id, matn } (id 1 dan), ol(id) → vazifa yoki undefined. Ikki amalga oshirish yozing: MassivOmbori (massiv) va MapOmbori (Map). Keyin shartnoma(nom, yaratuvchi) funksiyasi — har chaqiruvda yangi bo'sh ombor oladi, uch talabni tekshiradi va natijani chiqaradi: id 1 dan boshlanadi; ol yo'q id uchun undefined; ol qaytargan obyektni o'zgartirish ombordagini o'zgartirmaydi. Ishora: nusxa — { ...v } (Sayoz va chuqur nusxalash).
Yechim
class MassivOmbori {
#q = [];
async yarat(matn) {
const v = { id: this.#q.length + 1, matn };
this.#q.push(v);
return { ...v };
}
async ol(id) {
const v = this.#q.find((x) => x.id === id);
return v && { ...v };
}
}
class MapOmbori {
#q = new Map();
async yarat(matn) {
const v = { id: this.#q.size + 1, matn };
this.#q.set(v.id, v);
return { ...v };
}
async ol(id) {
return this.#q.get(id); // ataylab: nusxasiz
}
}
async function shartnoma(nom, yaratuvchi) {
const tekshir = async (talab, fn) => {
const ok = await fn(yaratuvchi());
console.log(`${ok ? "✓" : "✗"} ${nom}: ${talab}`);
};
await tekshir("id 1 dan",
async (o) => (await o.yarat("Non")).id === 1);
await tekshir("yo'q id — undefined",
async (o) => (await o.ol(99)) === undefined);
await tekshir("ol — nusxa", async (o) => {
await o.yarat("Non");
(await o.ol(1)).matn = "buzildi";
return (await o.ol(1)).matn === "Non";
});
}
await shartnoma("massiv", () => new MassivOmbori());
await shartnoma("map", () => new MapOmbori());Konsolda:
✓ massiv: id 1 dan
✓ massiv: yo'q id — undefined
✓ massiv: ol — nusxa
✓ map: id 1 dan
✓ map: yo'q id — undefined
✗ map: ol — nusxaMapOmbori dagi ataylab qoldirilgan xato — ichki obyektni to'g'ridan qaytarish — faqat shartnoma bilan ushlandi. Ikkala ombor "ishlaydi", lekin biri chaqiruvchiga o'z xotirasini ochib qo'ygan. Kanon shartnomasining birinchi testi aynan shu talabni tekshiradi: olingan.matn = "buzildi" — ombordagi o'zgarmasin.
3-mashq (qiyin): keshli o'ram
KeshliOmbor(ichki) yozing — xuddi shu interfeys (yarat, ol). ol — avval Map keshidan, bo'lmasa ichkisidan olib keshga qo'yadi; yarat — ichkisiga yozadi va butun keshni eskirgan qiladi. Eskirtirish uchun kalitlarni o'chirmang: avlod hisoblagichini oshiring va kalitni ${avlod}:${id} qiling. Ichki omborga murojaatlar sonini sanang. Sinov: 2-mashqdagi MassivOmbori ni o'rang, ol(1) ni ikki marta, keyin yarat, yana ol(1). Ishora: Caching naqshlari va invalidatsiya.
Yechim
class MassivOmbori {
#q = [];
async yarat(matn) {
const v = { id: this.#q.length + 1, matn };
this.#q.push(v);
return { ...v };
}
async ol(id) {
const v = this.#q.find((x) => x.id === id);
return v && { ...v };
}
}
class KeshliOmbor {
#ichki;
#kesh = new Map();
#avlod = 1;
murojaat = 0;
constructor(ichki) { this.#ichki = ichki; }
async ol(id) {
const kalit = `${this.#avlod}:${id}`;
if (this.#kesh.has(kalit)) return { ...this.#kesh.get(kalit) };
this.murojaat++;
const v = await this.#ichki.ol(id);
if (v !== undefined) this.#kesh.set(kalit, v);
return v && { ...v };
}
async yarat(matn) {
const v = await this.#ichki.yarat(matn);
this.#avlod++; // hamma eski kalit endi o'qilmaydi
return v;
}
}
const o = new KeshliOmbor(new MassivOmbori());
await o.yarat("Non");
await o.ol(1);
await o.ol(1);
console.log("ikki o'qish:", o.murojaat);
await o.yarat("Sut");
await o.ol(1);
console.log("yozuvdan keyin:", o.murojaat);
console.log(await o.ol(2));Konsolda:
ikki o'qish: 1
yozuvdan keyin: 2
{ id: 2, matn: 'Sut' }Ikkinchi ol(1) keshdan keldi — ichkiga murojaat 1 marta. yarat dan keyin avlod 2 bo'ldi: eski 1:1 kaliti endi so'ralmaydi, ol(1) yana ichkiga bordi. Kanondagi KeshliOmbor (27-qism) aynan shu g'oyada. Redis'da egasi bo'yicha avlod kaliti bor, yozuvda INCR — hamma o'qish kalitlari birdan eskiradi. Keyingi ikkinchi darsda Nest'ning CacheInterceptor i bilan solishtiramiz — u kalitni URL bo'yicha qo'yadi va bunday eskirtirishni bilmaydi.
12. Loyiha qadami: o'zgarish yo'q — tekshiruv
vazifalar-api o'zgarmaydi: repository naqshi API Nest'ga ko'chgan paytdan beri bor (interfeys va token), Dynamic modullar darsidan beri — OmborModule bilan tanlash. Bu dars uni nomladi va chegaralarini aniqladi.
O'z loyihangizda bajaring:
npm test -- omborBazasiz muhitda xotira va fayl omborlarining shartnoma testlari o'tadi, Postgres va Prisma testlari o'tkazib yuboriladi. Bazangiz bo'lsa (TEST_DATABASE_URL, TEST_PRISMA_DATABASE_URL) — «Biz ishga tushirdik» bo'limidagi 42 test. So'ng src/vazifalar/vazifalar.service.ts ni oching va tekshiring: unda pg, Prisma, redis importi yo'qmi? Bo'lmasligi kerak — bu chegaraning eng oddiy testi.
13. Real ishda
- Qatlamlar — har backend intervyusidagi savol: "controller, service, repository farqi nima?". Shu darsdagi uch savolli jadval — tayyor javob.
- Shartnoma testlari — ombor, to'lov provayderi, fayl saqlash kabi bir nechta amalga oshirishli joylarda kasbiy standart. 29-qismdagi to'lov adapterlari ham shu g'oyada.
- Modular monolit — modullar faqat interfeys orqali gaplashadi; repository naqshi uning kichik nusxasi (Modular monolit va hexagonal arxitektura).
- Review'dagi savollar: "bu xizmat nega
PrismaClientni import qilyapti?", "egalik tekshiruvi qayerda?".
Xulosa
- Controller — HTTP, xizmat — biznes qoidasi, ombor — saqlash; har biri faqat pastidagini biladi.
- Interfeys domen turida (
Vazifa,number, ISO sana): ORM turlari (BigInt, entity,P2025) tashqariga chiqmaydi. - "Topilmadi" va "konflikt" — ombor uchun natija (
YozishNatijasi), HTTP xatosiga xizmat aylantiradi. - Interfeys ish vaqtida yo'q —
Symboltoken +@Inject(VAZIFA_OMBORI). - Bitta shartnoma testi to'plami hamma omborni tekshiradi; biz 4 ombor × 5 testni PG 17 da o'tkazdik.
- Egalik — omborda: unutib bo'lmaydigan joyda.
Keyingi dars: Swagger: @nestjs/swagger — vazifalar-api uchun OpenAPI hujjati: /docs va /docs-json, Zod sxemalaridan konverter'siz.
Manbalar
- Martin Fowler — Repository (Patterns of Enterprise Application Architecture): https://martinfowler.com/eaaCatalog/repository.html
- NestJS hujjati — Custom providers (tokenlar, interfeys o'rnida): https://docs.nestjs.com/fundamentals/custom-providers
- Martin Fowler — Contract Test: https://martinfowler.com/bliki/ContractTest.html
- Kanon (IlmHamroh, 2026-10-27):
VazifaOmboriinterfeysi vaVAZIFA_OMBORItokeni,OmborModule, 5 amalga oshirish vaomborShartnomasi. Shartnoma testlari (xotira, fayl,pg, Prisma — 42 test) va formatlangan kodning tip tekshiruvi — biz ishga tushirdik (2026-11-02, PG 17, Vitest 5.0.5).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!