IlmHamroh
JavaScript Full-stack/30-qism. NestJS36/65-dars16 daqiqa
Mundarija (26)

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 Symbol token 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-api shu naqsh ustiga qurilgan.

Bu darsda

  • Controller, xizmat va ombor orasidagi chegarani aniq ayta olasiz: qaysi kod qayerda yashaydi.
  • Repository interfeysi va Symbol token 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):

ts
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, PrismaClient yo'q, pg.Pool yo'q.
  • BigInt yo'q: id: number. Prisma BigInt qaytaradi, pg — satr (TypeORM darsidagi bigint tuzog'i). Har ombor o'zi number ga aylantiradi.
  • Sana Date emas: Vazifa.yaratildi — ISO satr (UTC, Z bilan). Bazadan qaytgan timestamptz ham, 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:

ts
/** 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.yangila ichida Prisma P2025 (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:

ts
// 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" .-> P

Xizmat faqat o'rtadagi to'rtburchakni ko'radi. Pastdagi beshtasi — implements VazifaOmbori. Qaysi biri ishlashini OmborModule hal qiladi:

  • Ilovada — OmborModule.forRootAsync(): SAQLASH config qiymati bo'yicha (xotira, fayl, postgres, prisma) va REDIS_URL bo'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):

ts
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:

ts
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):

text
✓ 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

ts
// ❌ 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

ts
// ❌ 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

ts
// ❌ 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> bilan findAll, findById, save — ko'p maqolada tavsiya qilinadi. Kanon uni ishlatmaydi: VazifaOmbori ning metodlari vazifa domeniga xos (teglar, almashtirHammasi, kursor bilan royxat). 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):

  • ETag sarlavhasini qo'yish —
  • "1000 tadan ortiq vazifa bo'lmasin" qoidasi —
  • WHERE egasi_id = $1 sharti —
  • { 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
js
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:

text
✓ massiv: id 1 dan
✓ massiv: yo'q id — undefined
✓ massiv: ol — nusxa
✓ map: id 1 dan
✓ map: yo'q id — undefined
✗ map: ol — nusxa

MapOmbori 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
js
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:

text
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:

bash
npm test -- ombor

Bazasiz 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 PrismaClient ni 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 — Symbol token + @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

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
NestJS repository naqshi: interfeys, token va qatlamlar chegarasi — IlmHamroh