Модуль 15

Router-и та модульна архітектура

Ось готовий урок, створений за твоїм майстер-промптом.


🎓 CS50: Router-и та модульна архітектура

(або «Як не перетворити свій код на спагеті-монстра»)


1. 🔥 Вступ: проблема та мотивація

Привіт, друзі!

Уявіть, що ви вирішили відкрити величезний супермаркет. У вас є відділ овочів, відділ електроніки, пекарня та каса.

А тепер уявіть, що весь цей супермаркет — це одна-єдина гігантська кімната без перегородок і вказівників. Капуста лежить на ноутбуках, черга до каси змішалася з чергою за хлібом, а прибиральник не знає, де мити підлогу, бо все скрізь.

Хаос, правда?

У програмуванні ми часто починаємо писати код у файлі main.py (або app.js). Коли там 10 рядків — це чудово. Коли 50 — нормально. Але що стається, коли ваш проєкт розростається до 1000, 5000 рядків?

  • Чи зручно вам скролити 500 рядків униз, щоб знайти одну функцію реєстрації користувача?
  • Що станеться, якщо ви працюєте в команді, і двоє людей одночасно редагують цей гігантський файл? (Спойлер: Merge Conflict, і це боляче).

Навіщо нам ця тема? Сьогодні ми навчимося розрізати цей "гігантський файл" на логічні частини. Ми перетворимо "звалище" на організований "супермаркет" з чіткими відділами. Ми поговоримо про Router-и та Модульну архітектуру.


2. 🧠 Теоретична база (без сухої академічності)

Давайте розберемося з поняттями.

Що таке Модульність?

Це принцип "розділяй і володарюй". Замість одного файлу, який робить все (реєструє, продає, надсилає листи), ми створюємо окремі файли (модулі), кожен з яких відповідає за свою одну зону відповідальності.

  • users.py — займається тільки користувачами.
  • products.py — тільки товарами.
  • payments.py — тільки грошима.

Що таке Router (Маршрутизатор)?

Уявіть собі великий офісний центр. На вході сидить охоронець (ваш головний файл main.py). Ви підходите і кажете: "Мені треба у відділ кадрів". Охоронець не оформлює вас на роботу сам. Він просто каже: "Вам на 3-й поверх, кабінет 305".

Router — це і є цей "кабінет" або "відділ". Це міні-версія вашого додатку, яка обробляє тільки специфічні запити (маршрути).

Як це працює "під капотом"?

  1. Запит приходить на вхід: Клієнт стукає у /users/profile.
  2. Головний додаток (Main): Дивиться на URL. Бачить префікс /users.
  3. Делегування: Головний додаток каже: "О, це для Роутера Користувачів! Я передаю управління туди".
  4. Router: Отримує запит, знаходить потрібну функцію і повертає відповідь.

📌 Запам’ятайте: Головний файл (main) не повинен знати деталі реалізації. Він повинен знати лише хто за це відповідає.


3. 🧪 Приклади (від простого до реального)

Для прикладів використаємо Python (FastAPI), але логіка ідентична для Express.js, Django чи Flask.

Етап 1: Як ми писали раніше (The "Bad" Way)

Ось наш файл main.py. Все на купу.

# 📂 main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"message": "Hello World"}

# --- Логіка користувачів ---
@app.get("/users/")
def get_users():
    return [{"name": "Alice"}, {"name": "Bob"}]

@app.get("/users/me")
def get_current_user():
    return {"name": "Alice"}

# --- Логіка товарів ---
@app.get("/items/")
def get_items():
    return [{"item": "Laptop"}, {"item": "Mouse"}]

Питання до вас: Уявіть, що у нас 50 ендпоінтів для юзерів і 50 для товарів. Як буде виглядати цей файл?

Етап 2: Створюємо Роутер (The Modular Way)

Ми виносимо логіку користувачів в окремий файл.

# 📂 routers/users.py
from fastapi import APIRouter

# Створюємо "міні-додаток"
router = APIRouter()

@router.get("/users/")
def get_users():
    return [{"name": "Alice"}, {"name": "Bob"}]

@router.get("/users/me")
def get_current_user():
    return {"name": "Alice"}

Етап 3: Підключаємо Роутер до Головного файлу

Тепер main.py стає чистим і красивим. Він просто "реєструє" відділи.

# 📂 main.py
from fastapi import FastAPI
from routers import users # Імпортуємо наш файл

app = FastAPI()

# Підключаємо роутер!
app.include_router(users.router)

@app.get("/")
def root():
    return {"message": "Welcome to the API Mall"}

Результат: Запити на /users/ так само працюють, але код тепер структурований. Якщо треба змінити щось у юзерах — ви не зламаєте товари.


4. 🛠 Практична частина

Час "забруднити руки" кодом!

🔹 Завдання 1: Рефакторинг (Copy-Paste) Створіть файл routers/items.py. Перенесіть туди логіку товарів з "поганого" прикладу вище. Підключіть його в main.py.

🔹 Завдання 2: Префікси (DRY - Don't Repeat Yourself) У файлі routers/users.py ми скрізь пишемо /users/.... Змініть підключення в main.py так, щоб префікс задавався один раз при підключенні: app.include_router(users.router, prefix="/users") А в самому роутері приберіть слово /users зі шляхів. Що станеться, якщо ви цього не зробите?

🔹 Завдання 3: "Пошта зламалася" (Debug) Ви створили файл routers/orders.py, написали там ендпоінти, але коли запускаєте сервер і йдете на /orders, отримуєте помилку 404 Not Found. Питання: Який один критично важливий рядок ви забули додати в main.py?

🔹 Завдання 4: Міні-кейс Ви робите сайт для онлайн-школи. Які файли-роутери ви б створили? (Підказка: подумайте про сутності — Студенти, Курси, Оцінки).

🔹 Завдання 5: А що, якщо...? Клієнт просить випустити нову версію API, але стару поки не видаляти. Як за допомогою роутерів і префіксів зробити так, щоб працювало і /api/v1/users і /api/v2/users, не створюючи хаос?


5. 💡 Мислення як у розробника

Як відрізнити новачка від профі в цій темі?

  1. Circular Imports (Циклічні імпорти):

    • Помилка: У users.py ви імпортуєте щось з items.py, а в items.py — з users.py. Python (і JS) збожеволіє і впаде.
    • Як думає профі: "Якщо двом модулям потрібна одна й та сама логіка (наприклад, функція надсилання email), я винесу її в третій незалежний файл utils.py або services.py".
  2. Групування за змістом, а не за типом:

    • Помилка: Складати всі "контролери" в одну папку, а всі "моделі" в іншу. Це називається "Architecture by Layer".
    • Як думає профі: Краще групувати за Feature (Фічею). Папка Users має містити все про юзерів (роутер, базу даних, схеми). Це робить систему дійсно модульною. Якщо ви хочете видалити "юзерів", ви просто видаляєте одну папку.
  3. Головний файл — це лише точка входу.

    • У main.py не має бути бізнес-логіки. Взагалі. Тільки налаштування і підключення роутерів.

6. 🧩 Підсумок

Отже, що ми сьогодні зробили? Ми взяли величезну купу коду і розклали її по поличках.

  • Тепер ви знаєте, що Router — це окремий відділ вашого додатку.
  • Ви вмієте використовувати include_router, щоб збирати додаток як конструктор LEGO.
  • Ви розумієте, що чистий код — це не просто естетика, це можливість масштабування.

Що ви тепер вмієте? Ви можете будувати бекенд не для "лабораторної роботи", а для реального стартапу, який можна розширювати роками.

🔜 Тизер наступного уроку: Окей, ми розділили маршрути. Але всі наші дані зараз просто "висять" у пам'яті або повертаються як статика. Як нам підключити ці роутери до справжньої Бази Даних і не наробити помилок з підключеннями? Про це — наступного разу.

А поки що — happy coding!