6-bo‘lim
Dasturiy arxitektura
Qatlamli, monolit va mikroservis arxitekturalar, bog'liqlik va yaxlitlik, arxitektura qarorlarini hujjatlashtirish.
Ushbu bo‘lim mundarijasi
Arxitektura - tizimning eng qimmat o'zgaradigan qarorlari to'plami. Kodni har kuni o'zgartirish mumkin; arxitekturani esa - deyarli hech qachon.
Arxitektura nima? #
Dasturiy arxitektura - tizimning asosiy komponentlari, ular orasidagi munosabatlar va ularni belgilaydigan prinsiplar.
Amaliy ta'rif: *"arxitektura - keyinchalik o'zgartirish qiyin bo'lgan qarorlar."*
Ikki asosiy tushuncha #
// Yuqori bog'liqlik - yomon
class BuyurtmaServisi
{
public function yarating(array $malumot): void
{
$pdo = new PDO('mysql:host=localhost;dbname=dokon', 'root', '');
$pdo->exec("INSERT INTO buyurtmalar ...");
mail($malumot['email'], 'Buyurtma', 'Rahmat!');
file_put_contents('/var/log/buyurtma.log', date('c') . " yaratildi\n", FILE_APPEND);
}
}
Bu klass hamma narsani biladi: baza turini, ulanish ma'lumotlarini, email yuborish usulini, jurnal fayli joylashuvini.
Uni testlab bo'lmaydi, baza yoki email provayderini almashtirib bo'lmaydi.
// Past bog'liqlik - yaxshi
class BuyurtmaServisi
{
public function __construct(
private BuyurtmaRepozitoriyInterfeysi $repozitoriy,
private XabarnomaInterfeysi $xabarnoma,
private LoggerInterface $jurnal,
) {}
public function yarating(BuyurtmaSorovi $sorov): Buyurtma
{
$buyurtma = $this->repozitoriy->saqlang(
Buyurtma::yangi($sorov->mijozId, $sorov->elementlar)
);
$this->xabarnoma->yuboring(
$sorov->email,
new BuyurtmaYaratildi($buyurtma)
);
$this->jurnal->info('Buyurtma yaratildi', ['id' => $buyurtma->id]);
return $buyurtma;
}
}
- Baza turini almashtirish - faqat repozitoriy o'zgaradi
- Email o'rniga SMS - faqat xabarnoma o'zgaradi
- Test yozish - soxta obyektlar beriladi, haqiqiy baza kerak emas
- Klass nima qilishini bildiradi, qanday qilishini emas
Qatlamli arxitektura #
Eng keng tarqalgan tuzilma.
// Taqdimot qatlami
class BuyurtmaKontrolleri
{
public function __construct(private BuyurtmaServisi $servis) {}
public function yarating(Request $sorov): Response
{
$malumot = $sorov->validate([
'mahsulot_id' => 'required|integer',
'soni' => 'required|integer|min:1',
]);
$buyurtma = $this->servis->yarating(
BuyurtmaSorovi::sorovdan($malumot)
);
return new JsonResponse(['id' => $buyurtma->id], 201);
}
}
// Biznes mantiq qatlami
class BuyurtmaServisi
{
public function __construct(
private BuyurtmaRepozitoriyInterfeysi $buyurtmalar,
private MahsulotRepozitoriyInterfeysi $mahsulotlar,
) {}
public function yarating(BuyurtmaSorovi $sorov): Buyurtma
{
$mahsulot = $this->mahsulotlar->toping($sorov->mahsulotId);
if ($mahsulot === null) {
throw new MahsulotTopilmadi($sorov->mahsulotId);
}
if (!$mahsulot->zaxiraYetarlimi($sorov->soni)) {
throw new ZaxiraYetarliEmas($mahsulot, $sorov->soni);
}
return $this->buyurtmalar->saqlang(
Buyurtma::yangi($sorov->mijozId, $mahsulot, $sorov->soni)
);
}
}
// Ma'lumotga kirish qatlami
class MysqlBuyurtmaRepozitoriy implements BuyurtmaRepozitoriyInterfeysi
{
public function __construct(private PDO $pdo) {}
public function saqlang(Buyurtma $buyurtma): Buyurtma
{
$stmt = $this->pdo->prepare(
'INSERT INTO buyurtmalar (mijoz_id, jami, holat) VALUES (?, ?, ?)'
);
$stmt->execute([$buyurtma->mijozId, $buyurtma->jami, $buyurtma->holat]);
return $buyurtma->idBilan((int) $this->pdo->lastInsertId());
}
}
// XATO: kontroller to'g'ridan-to'g'ri bazaga murojaat qilyapti
class BuyurtmaKontrolleri
{
public function korsating(int $id): Response
{
$pdo = new PDO(/* ... */);
$buyurtma = $pdo->query("SELECT * FROM buyurtmalar WHERE id = $id");
// ...
}
}
Bir marta shunday qilinsa, keyingi safar ham qilinadi. Olti oydan keyin qatlamlar butunlay yo'qoladi.
Monolit va mikroservislar #
| Monolit | Mikroservislar | |
|---|---|---|
| Boshlash | Oson | Qiyin |
| Deploy | Bitta | O'nlab |
| Masshtablash | Butun tizim | Faqat kerakli qism |
| Texnologiya | Bitta | Har xil bo'lishi mumkin |
| Nosozlik | Butun tizim | Faqat bitta xizmat |
| Tranzaksiya | Oddiy | Juda murakkab |
| Nosozlikni topish | Oson | Qiyin (taqsimlangan) |
| Jamoa | Bitta | Har xizmatga alohida |
| Infratuzilma | Sodda | Kubernetes, monitoring, service mesh |
Yangi loyihada mikroservis arxitekturasini tanlash - eng ko'p uchraydigan xatolardan biri.
Nima uchun?
- Boshida domen chegaralari noma'lum - xizmatlarni noto'g'ri bo'lasiz
- Taqsimlangan tizimning murakkabligi ulkan: tarmoq xatolari, izchillik, monitoring
- Kichik jamoa buni qo'llab-quvvatlay olmaydi
To'g'ri yo'l: yaxshi modullashtirilgan monolitdan boshlang. Chegaralar aniq bo'lgach, kerakli qismlarni ajratib chiqarasiz.
Bu "modulli monolit" yondashuvi deb ataladi.
- Jamoa 50+ dasturchidan iborat va bir-biriga xalaqit beryapti
- Ba'zi qismlar boshqalardan 10-100 barobar ko'proq yuk oladi
- Turli qismlar uchun turli texnologiya kerak
- Alohida deploy tsikllari zarur
- Sizda kuchli DevOps madaniyati bor
Agar bu shartlar bajarilmasa - monolit yaxshiroq.
Modulli monolit #
manba/
├── Modullar/
│ ├── Katalog/
│ │ ├── Domen/
│ │ ├── Ilova/
│ │ ├── Infratuzilma/
│ │ └── KatalogAPI.php ← faqat shu orqali murojaat
│ ├── Savat/
│ │ ├── Domen/
│ │ ├── Ilova/
│ │ └── SavatAPI.php
│ └── Buyurtma/
│ ├── Domen/
│ ├── Ilova/
│ └── BuyurtmaAPI.php
└── Umumiy/
// XATO: modul ichki qismiga to'g'ridan-to'g'ri murojaat
use App\Modullar\Katalog\Domen\Mahsulot;
use App\Modullar\Katalog\Infratuzilma\MysqlMahsulotRepozitoriy;
// TO'G'RI: faqat ochiq API orqali
use App\Modullar\Katalog\KatalogAPI;
class SavatServisi
{
public function __construct(private KatalogAPI $katalog) {}
public function qoshing(int $mahsulotId, int $soni): void
{
$mahsulot = $this->katalog->mahsulotMalumoti($mahsulotId);
// ...
}
}
Siz mikroservislarning tashkiliy foydasini olasiz (aniq chegaralar, mustaqil jamoalar), lekin texnik murakkabligini olmaysiz.
Keyinchalik biror modulni alohida xizmatga ajratish kerak bo'lsa - chegaralar allaqachon tayyor.
Boshqa arxitektura uslublari #
Olti burchakli (portlar va adapterlar) #
Biznes mantiq hech qanday tashqi texnologiyani bilmaydi.
U MySQL yoki PostgreSQL ekanini, HTTP yoki CLI orqali chaqirilishini bilmaydi. Faqat interfeyslar bilan ishlaydi.
Natijada: testlash oson, texnologiya almashtirish arzon.
Hodisalarga asoslangan arxitektura #
// Buyurtma yaratilganda hodisa e'lon qilinadi
class BuyurtmaServisi
{
public function yarating(BuyurtmaSorovi $sorov): Buyurtma
{
$buyurtma = $this->repozitoriy->saqlang(/* ... */);
$this->hodisalar->elon(new BuyurtmaYaratildi($buyurtma));
return $buyurtma;
}
}
// Turli tinglovchilar mustaqil javob beradi
class EmailYuboruvchi
{
public function buyurtmaYaratildi(BuyurtmaYaratildi $hodisa): void
{
$this->pochta->yuboring(/* ... */);
}
}
class ZaxiraYangilovchi
{
public function buyurtmaYaratildi(BuyurtmaYaratildi $hodisa): void
{
$this->ombor->kamaytiring(/* ... */);
}
}
class StatistikaYozuvchi
{
public function buyurtmaYaratildi(BuyurtmaYaratildi $hodisa): void
{
$this->hisoblagich->oshiring('buyurtmalar');
}
}
Yangi xatti-harakat qo'shish uchun BuyurtmaServisi ni o'zgartirish
shart emas - shunchaki yangi tinglovchi qo'shiladi.
Kamchiligi: oqimni kuzatish qiyinlashadi. "Bu email qayerdan yuborildi?" degan savolga javob topish uchun barcha tinglovchilarni ko'rish kerak.
Arxitektura qarorlarini hujjatlashtirish #
# ADR-007: Savat ma'lumotini Redis da saqlash
**Sana:** 2026-08-12
**Holat:** Qabul qilindi
**Qaror qabul qiluvchilar:** Husanboy (arxitektor), Jasur (backend)
## Kontekst
Savat ma'lumoti hozir MySQL da saqlanadi. Yuk oshgani sari
`savat_elementlari` jadvaliga yozish sekinlashmoqda.
O'lchovlar:
- Savatga qo'shish: o'rtacha 180 ms
- Cho'qqi paytida: 900 ms gacha
- Savat ma'lumoti 90% hollarda 24 soatdan ko'p yashamaydi
## Ko'rib chiqilgan variantlar
1. **MySQL da qoldirish + indekslar** - qisman yordam beradi, lekin
yozish yuki qoladi
2. **Redis** - tez, TTL qo'llab-quvvatlaydi
3. **Brauzer localStorage** - qurilmalar orasida sinxronlashmaydi
## Qaror
Savat ma'lumotini **Redis** da saqlaymiz, TTL 30 kun.
Buyurtma berilganda ma'lumot MySQL ga ko'chiriladi.
## Oqibatlari
**Ijobiy:**
- Savat amallari 5-10 ms
- MySQL yuki kamayadi
- TTL avtomatik tozalash
**Salbiy:**
- Yangi infratuzilma komponenti (Redis klasteri)
- Redis o'chsa savatlar yo'qoladi (qabul qilamiz)
- Jamoa Redis ni o'rganishi kerak
## Muqobil yo'l
Redis ishlamasa, MySQL ga qaytish uchun `SavatOmborInterfeysi`
saqlanadi.
Bir yildan keyin yangi dasturchi so'raydi: "nima uchun savat Redis da? MySQL da bo'lgani yaxshiroq emasmi?"
ADR shu savolga javob beradi va bir xil bahsni qayta boshlashning oldini oladi.
Har bir muhim arxitektura qarori uchun bittadan ADR yozing.
Arxitektura xatolari #
| Xato | Oqibati |
|---|---|
| Erta optimizatsiya | Kerak bo'lmagan murakkablik |
| Kech arxitektura | Texnik qarz, qayta yozish |
| Modaga ergashish | "Hamma mikroservis ishlatadi" |
| Universal yechim | Har bir vaziyatga bir xil naqsh |
| Hujjatsizlik | Bilim faqat bir odamning boshida |
| Ortiqcha abstraksiya | 5 qatlam interfeys, bitta amalga oshirish |
| Katta qayta yozish | "Noldan yozamiz" - deyarli har doim muvaffaqiyatsiz |
// Bitta amalga oshirish uchun uchta qatlam
interface FoydalanuvchiFabrikasiInterfeysi { }
abstract class AbstraktFoydalanuvchiFabrikasi implements FoydalanuvchiFabrikasiInterfeysi { }
class OddiyFoydalanuvchiFabrikasi extends AbstraktFoydalanuvchiFabrikasi { }
Interfeys hozir kerak bo'lganda yarating, "kelajakda kerak bo'lar" degan taxmin bilan emas.
Ikkinchi amalga oshirish paydo bo'lganda interfeys chiqarish - 5 daqiqalik ish.
- Yuqoridagi "yuqori bog'liqlik" misolini past bog'liqlikka aylantiring.
- O'z loyihangizni qatlamlarga bo'ling va sxemasini chizing.
- Qatlam chegarasi buzilgan joyni toping.
- Monolit va mikroservis afzalliklarini jadval qilib yozing.
- Onlayn do'kon uchun modulli monolit tuzilmasini loyihalang.
- Har bir modul uchun ochiq API metodlarini yozing.
- Olti burchakli arxitektura uchun uchta interfeys yozing.
- Hodisalarga asoslangan yondashuv bilan bitta stsenariy yozing.
- Uchta ADR yozing: baza tanlovi, autentifikatsiya, kesh.
- Loyihangizdagi ortiqcha abstraksiyani toping va soddalashtiring.
Xulosa #
- Arxitektura - keyinchalik o'zgartirish qiyin bo'lgan qarorlar.
- Ikki asosiy mezon: modullar orasida past bog'liqlik, modul ichida yuqori yaxlitlik.
- Qatlamli arxitektura - eng keng tarqalgan; qatlamlarni chetlab o'tmang.
- Monolit - deyarli har doim to'g'ri boshlang'ich tanlov.
- Mikroservislar tashkiliy muammoni hal qiladi, texnik murakkablik qo'shadi.
- Modulli monolit - ikkalasining o'rtasidagi eng amaliy yechim.
- Olti burchakli arxitektura domen mantiqini texnologiyadan ajratadi.
- Hodisalar yangi xatti-harakatni mavjud kodni o'zgartirmasdan qo'shish imkonini beradi.
- ADR yozing - bir xil bahsni qayta boshlamaslik uchun.
- Erta optimizatsiya va ortiqcha abstraksiya dan qoching.
Keyingi bo'limda loyihalash naqshlarini o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.