Asosiy kontentga o‘tish

Maqolalar

Maqola · 2026-08-16 · ~7 daqiqa o‘qiladi · o'rta

Katalog botda emas, backend'da yashaydi: besh yil ishlagan arxitektura

#python #aiogram #telegram-bot #api #arxitektura

Mundarija

Aladdin bot 2021-yilda yozilgan va hozirgacha ishlab turibdi. Qizig'i shundaki, shu besh yil ichida katalog minglab marta yangilangan, narxlar necha bor o'zgargan, yangi kategoriyalar qo'shilgan — lekin botning kodiga deyarli tegilmagan. Bugun ana shu natijani bergan arxitekturani ko'rib chiqamiz.

Birinchi yo'l va uning oqibati#

Katalog boti yozayotgan odam odatda eng qisqa yo'ldan boradi: tovarlarni botning o'z bazasiga saqlaydi. Boshida bu juda qulay tuyuladi, chunki hamma narsa bitta joyda va qo'shimcha xizmat kerak emas:

# Birinchi qarashda jozibali, lekin keyin qimmatga tushadigan yo'l:
# tovarlar botning SQLite bazasida
async def show_products(message: types.Message):
    products = db.execute("SELECT * FROM products WHERE category_id = ?", (cat_id,))

Muammo bir-ikki oydan keyin boshlanadi. Menejer yangi tovar qo'shmoqchi — buning uchun dasturchiga yozadi. Narx o'zgardi — yana dasturchiga yozadi. Rasm almashtirish kerak — yana o'sha. Endi siz bot yozgan dasturchi emas, kompaniyaning kontent-operatorisiz. Aladdin'dagi kabi assortiment katta bo'lsa, bu yo'l bilan yashab bo'lmaydi.

To'g'ri yo'l: bot — bu ko'zgu#

Yechim shuki, ma'lumot va uni ko'rsatish bir-biridan ajratiladi. Tovarlar, kategoriyalar, narxlar va manzillar veb-backend'da yashaydi, u yerda menejerlar uchun oddiy admin-panel bor. Bot esa hech narsani o'zida saqlamaydi: kerak bo'lganda API'dan so'rab oladi va foydalanuvchiga ko'rsatadi, xolos.

Menejer ──► Admin-panel ──► Backend bazasi
                                 │
                            REST API
                                 │
Foydalanuvchi ──► Telegram bot ──┘  (faqat o'qiydi)

Shu ajratishdan keyin ikkala tomon o'z sur'atida yashaydi: katalog har kuni o'zgaradi, bot esa yillab o'zgarmaydi. Aynan shuning uchun 2021-yilgi kod 2026-yilda ham xizmat qilyapti.

Bot tomoni: API mijoz qatlami#

Loyihada API bilan ishlaydigan kodni bitta papkaga yig'amiz, handler'lar esa undan foydalanadi:

bot/
├── api/
│   ├── __init__.py
│   └── catalog.py      ← backend bilan gaplashadigan yagona joy
├── handlers/
│   └── products.py     ← foydalanuvchiga ko'rsatish
└── app.py
# bot/api/catalog.py
import aiohttp

BASE_URL = "https://catalog.misol.uz/api/telegram"
TIMEOUT = aiohttp.ClientTimeout(total=5)  # backend javob bermasa 5 soniyada taslim


class CatalogAPI:
    """Backend'dagi katalogga read-only darcha.

    Bot uchun backend — tashqi xizmat. Shuning uchun har chaqiruvda timeout
    bor va xatolik alohida turga o'raladi: handler'lar aiohttp'ni bilmaydi.
    """

    async def _get(self, path: str, **params) -> dict | list:
        async with aiohttp.ClientSession(timeout=TIMEOUT) as session:
            async with session.get(f"{BASE_URL}{path}", params=params) as resp:
                resp.raise_for_status()
                return await resp.json()

    async def categories(self) -> list[dict]:
        return await self._get("/categories/")

    async def products(self, category_id: int) -> list[dict]:
        return await self._get("/products/", category_id=category_id)

    async def product(self, product_id: int) -> dict:
        return await self._get(f"/products/{product_id}/")


catalog = CatalogAPI()

Handler endi juda sodda bo'lib qoladi, chunki uning ishi faqat ko'rsatish:

# bot/handlers/products.py
from aiogram import Router, types
from aiogram.exceptions import TelegramBadRequest

from bot.api.catalog import catalog

router = Router()


@router.callback_query(lambda c: c.data.startswith("cat:"))
async def show_category(callback: types.CallbackQuery):
    category_id = int(callback.data.split(":")[1])
    try:
        products = await catalog.products(category_id)
    except Exception:
        # backend yotgan payt bot ham "yotib qolmasin":
        # foydalanuvchiga halol javob beramiz va admin'ga xabar ketadi
        await callback.message.answer(
            "Katalog vaqtincha ishlamayapti, birozdan keyin urinib ko'ring."
        )
        return
    # ... tovarlar ro'yxatini tugmalar bilan chiqarish (2-qismda batafsil)

Backend yotsa, bot nima qiladi?

Ajratilgan arxitekturaning narxi shu savol. Bot endi tashqi xizmatga bog'liq, demak u xizmat yotgan paytga tayyor turishi kerak: har so'rovda timeout, xatoda foydalanuvchiga tushunarli javob, adminlarga bildirishnoma. Bular bo'lmasa, backend'dagi besh daqiqalik uzilish "bot ishlamayapti" degan yuzlab shikoyatga aylanadi.

Backend tomoni: bot uchun alohida, kichik API#

Backend'da admin-panel qanday bo'lishi muhim emas — Django admin ham, tayyor CMS ham bo'laveradi. Muhimi, bot uchun alohida, kichik endpoint'lar ochib qo'yishdir. Aladdin'da bu /api/telegram/... yo'llari: ularda faqat botga kerak maydonlar bor va hech qanday yozish yo'q.

# backend tomonida (Django misolida)
# catalog/views.py
from django.http import JsonResponse

from .models import Product


def telegram_products(request):
    category_id = request.GET.get("category_id")
    products = Product.objects.filter(category_id=category_id, is_active=True).values(
        "id", "name", "price", "image_url"
    )
    # faqat botga kerak maydonlar — ichki narx, qoldiq, tannarx bu yerga chiqmaydi
    return JsonResponse(list(products), safe=False)

Nega umumiy admin API'ni bermay, alohida qilamiz? Chunki bot — ochiq dunyoga qaragan mijoz. Unga qancha kam ma'lumot chiqsa, xatolik ehtimoli shuncha kam: tannarx, qoldiq, ichki izohlar kabi maydonlar tasodifan ham botga oqib tushmaydi.

Kesh haqida ikki og'iz

Kategoriyalar ro'yxati kuniga bir marta o'zgaradi, bot esa uni har bosishda so'raydi. Oddiy yechim — API mijozida qisqa muddatli kesh (masalan, 60 soniya). Bu backend'dagi yukni sezilarli kamaytiradi, foydalanuvchi esa farqni sezmaydi. Keshlash strategiyalari haqida alohida yozganman: Redis keshlash.

Bu arxitektura qachon ortiqcha?#

Halol javob: har doim ham kerak emas. Agar botingiz o'n-o'n besh tovarli kichik do'kon uchun bo'lsa va kontentni o'zingiz yangilab tursangiz, alohida backend ko'tarish — ortiqcha dabdaba. Ajratish o'zini oqlashi uchun ikki shart bo'lishi kerak: kontentni dasturchi bo'lmagan odam boshqarishi va kontent tez-tez o'zgarishi. Aladdin'da ikkalasi ham bor edi, shuning uchun arxitektura besh yildan beri o'zini oqlab kelyapti.

Xulosa#

Bot va kontentni ajratish — katta kompaniyalar uchun emas, uzoq yashashi kerak bo'lgan har qanday bot uchun qoida. Bot ko'rsatadi, backend saqlaydi, menejer admin-paneldan boshqaradi. Bot tashqi xizmatga bog'liq bo'lgani uchun timeout va halol xato-javoblar shart. Botga esa umumiy emas, maxsus kichik API ochiladi — ortiqcha ma'lumot tashqariga chiqmaydi.

Keyingi qismda shu katalogni foydalanuvchiga chiroyli ko'rsatishni o'rganamiz: kategoriya ichiga kirib, tovarni ochib, yana ortga qaytadigan ichma-ich tugmalar navigatsiyasi.